3

AndroidPlotで値のピクセル位置を取得しようとしていますが、機能しません。アイデアは、特定の用語で、プロットに示されている正確なポイントにカーソルを置くことです。同様の問題に遭遇/解決した人はいますか?

よろしくジェスパー

4

3 に答える 3

3

より優れた組み込みソリューションがあるかどうかはわかりませんが、手動によるアプローチを次に示します。
次のコードは、ユーザーが画面に触れた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;
    }
});
于 2013-09-05T19:17:54.670 に答える
2

私は解決策を見つけました。私の意図は、特定のピクセルの値を見つけることでした(たとえば、プロットをタップすることで、表現する値が必要でした)。それで、少し検索した後、私はヘルパークラスの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()はこれを行います。その場合、上記のコードと非常によく似ています。

于 2012-08-24T09:01:32.767 に答える
0

更新されたソリューション

私は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.トレースに十分近づいた場合は、マーカー/カーソルを移動します

その他のヒント

  • プロットに含めることができるマーカーの数に制限はありませんが、カーソルを1つしか持つことができないため、この例ではカーソルの代わりにマーカーを使用しています。

  • この関数はマーカーの位置を変更するだけです。オプションで、最初にマーカーをフォーマットしてプロットに追加する必要があります。

  • 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");
    }
}
于 2020-01-17T04:16:31.510 に答える