我试图在我的 AndroidPlot 中获取一个值的像素位置,但我无法让它工作。这个想法是将光标放置在图上显示的确切点上,在特定条件下。你们中有人遇到/解决过类似的问题吗?
问候杰斯珀
我试图在我的 AndroidPlot 中获取一个值的像素位置,但我无法让它工作。这个想法是将光标放置在图上显示的确切点上,在特定条件下。你们中有人遇到/解决过类似的问题吗?
问候杰斯珀
我不知道是否有更好的内置解决方案,但这是一种手动方法。
以下代码将光标放置在用户触摸屏幕的 X 坐标和绘图的第一个数据系列的相应 Y 坐标上。
//NOTE about XYPlotZoomPan: when an OnTouchListener is set, zooming is disabled. Subclass to avoid it.
mPlot.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent me) {
float touchX = me.getX();
float touchY = me.getY();
XYGraphWidget widget = mPlot.getGraphWidget();
RectF gridRect = widget.getGridRect();
if(gridRect.contains(touchX, touchY)){ //Check the touch event is in the grid
XYSeries xyData = mPlot.getSeriesSet().iterator().next();
long targetValX = Math.round(widget.getXVal(touchX));
Log.d(TAG, "Touched at " + touchX + ", " + touchY + ". Target val X: " + targetValX);
Long targetValY = null;
Long prevValX = null;
if(mPlot.getSeriesSet().size() > 1){
Log.w(TAG, "More than one series in plot. Using only the first one");
}
for(int i = 0; i < xyData.size(); ++i){
long currValX = xyData.getX(i).longValue();
long currValY = xyData.getY(i).longValue();
//Calculate the range value of the closest domain value (assumes xyData is sorted in ascending X order)
if(currValX >= targetValX){
long currDiff = currValX - targetValX;
if(prevValX != null && (targetValX - prevValX) < currDiff){
targetValY = xyData.getY(i-1).longValue();
}else{
targetValY = currValY;
}
break;
}
prevValX = currValX;
}
if(targetValY != null){
long maxValY = mPlot.getCalculatedMaxY().longValue();
long minValY = mPlot.getCalculatedMinY().longValue();
float pixelPosY = gridRect.top + ValPixConverter.valToPix(
(double)targetValY, (double)minValY, (double)maxValY, (float)gridRect.height(), true);
widget.setRangeCursorPosition(pixelPosY);
widget.setDomainCursorPosition(touchX);
Log.d(TAG, String.format("Domain cursor set at Y %.2f, val %.2f = %d, min-maxValY (%d, %d)",
pixelPosY,
widget.getRangeCursorVal(), targetValY,
minValY, maxValY));
}else{
Log.w(TAG, "Couldn't find the closest range to the selected domain coordinate");
}
mPlot.invalidate();
}else{
Log.d(TAG, "Touched outside the plot grid");
}
return false;
}
});
我找到了解决方案。我的意图是找到给定像素的值(例如,通过点击图,我想要表示值)。所以,经过一番搜索,我找到了助手类 ValPixConverter。它提供了一些适合我需要的方法。不幸的是,没有这些方法的文档,但我找到了一个解决方案:
private float pixelToValueY(float y) {
//Parameters: 1=input y-value, 2=minmal y-value that is shown, 3=maximal y-value that is shown, 4=Hight of the view, 5=flip
return (float) ValPixConverter.pixToVal(y, minXY.y, maxXY.y, mySimpleXYPlot.getHeight(), false);
}
private float pixelToValueX(float x) {
//Parameters: 1=input y-value, 2=minmal y-value that is shown, 3=maximal y-value that is shown, 4=Hight of the view, 5=flip
return (float) ValPixConverter.pixToVal(x, minXY.x, maxXY.x, mySimpleXYPlot.getWidth(), false);
}
你只需要另一种方式。方法 valToPix() 将执行此操作。它与上面的代码非常相似。
我尝试遵循 rbarriuso 方法,但我发现它已经过时且过于复杂。
使用 rbarriuso 答案作为指导,我想出了以下内容:
private void placeMarkerOnGraph(XYPlot plot, XYSeries plotSeries, XValueMarker xMarker, YValueMarker yMarker, float touchX, float touchY) {
if(plot.getRegistry().getSeriesList().contains(plotSeries)) {
if (plot.getGraph().getGridRect().contains(touchX, touchY)) {
int targetValX = plot.screenToSeriesX(touchX).intValue();
int targetValY = plot.screenToSeriesY(touchY).intValue();
int threshold = (int)(plot.getBounds().getHeight().intValue() * 0.1f); //How close to a trace does the user have to click to move the markers?
if (plotSeries.getY(targetValX).intValue() - targetValY < threshold) {
Log.w(TAG, "Touched within threshold to trace line");
xMarker.setValue(targetValX);
yMarker.setValue(plotSeries.getY(targetValX).intValue());
}
} else {
Log.w(TAG, "Touched outside graph");
}
} else {
Log.w(TAG, "Plot does not contain series provided");
}
}
步骤 1. 检查您是否实际触摸了图表内部
步骤 2. 从触摸线转换为屏幕图形坐标
步骤 3. 取 X 值(已转换为图形坐标)并找到它的 Y 值
第 4 步。如果您触摸的距离足够近,请移动标记/光标
我在示例中使用标记而不是光标,因为一个绘图可以有多少个标记没有限制,但您只能有一个光标。
该功能仅更改标记位置,您必须先选择格式化并将标记添加到绘图中。
XYSeries 现在使用数字,因此请使用必要的值,例如:如果您的系列从0.0f
使用1.0f
.floatValue()
在您使用之前,任何东西都不会在视觉上发生变化plot.redraw()
阈值是考虑缩放的绘图总高度的百分比
第一个解决方案适用于不是很陡峭的图形,但在其他方面它没有很高的准确性,因为它没有考虑不直接低于用户在图形上触摸的数据点。
下面我添加了一个更强大的解决方案,它依赖于 for 循环,但更准确。它的工作原理与第一个解决方案非常相似。
private void placeMarkerOnGraph(XYPlot plot, XYSeries plotSeries, XValueMarker xMarker, YValueMarker yMarker, float touchX, float touchY) {
if(plot.getRegistry().getSeriesList().contains(plotSeries)) {
if (plot.getGraph().getGridRect().contains(touchX, touchY)) {
int targetValX = plot.screenToSeriesX(touchX).intValue();
int targetValY = plot.screenToSeriesY(touchY).intValue();
int threshold = (int)(plot.getBounds().getWidth().intValue() * 0.1f); //How close to a trace does the user have to click to move the markers?
Integer closestValue = null;
Integer xPos = null;
Integer yPos = null;
for(int i = targetValX - threshold; i < targetValX + threshold; i++){
//If the user touches near either end of the graph we need to make sure we don't go out of the arrays bounds
if(i > 0 && i < graphSize) {
int closeness = Math.abs(Math.abs(plotSeries.getY(i).intValue() - targetValY) + Math.abs(i - targetValX));
Log.d(TAG,"Distance to nearest trace: " + closeness);
if ((closestValue != null && closeness < closestValue) || (closestValue == null && closeness < threshold)) {
closestValue = closeness;
xPos = i;
yPos = plotSeries.getY(i).intValue();
}
}
}
if (xPos != null) {
Log.d(TAG, "Touched within threshold");
xMarker.setValue(xPos);
yMarker.setValue(yPos);
} else {
Log.d(TAG, "Touched outside threshold");
}
} else {
Log.w(TAG, "Touched outside graph");
}
} else {
Log.w(TAG, "Plot does not contain series provided");
}
}