在 Android 上使用画图绘制自定义折线图

Drawing a custom line graph with Paint on Android

我正在尝试在 Android 应用程序

上使用 Canvas and Paint 绘制折线图

首先,我使用 generateData() 生成一些数据点,它为 Y 数据点创建随机值,并为 X 数据点创建 i 50 倍的随机值。

我希望每个 X 点被 50 个像素分隔(作为一个比例),因此绘制了一个类似的图形,如下所示:

申请class

    public class Plotter extends View {

    private List<Float> xPosList, yPosList;
    private List<Path> pathList;
    private Path path;
    private Paint paint;

    private ConstraintLayout cl;
    private TextView stockPriceView;


    public Plotter(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        this.xPosList = new ArrayList<>();
        this.yPosList = new ArrayList<>();
        this.pathList = new ArrayList<>();
        this.paint = new Paint();
        this.paint.setStrokeWidth(20);
        this.paint.setColor(Color.GREEN);


        generateData();
    }

    /***
     * Generates random float data points from 5 to 100 and creates a path to plot
     */
    private void generateData() {

        int min = 5;
        int max = 100;
        double random = 0;

        float xPos = 0;
        float yPos = 0;

        for (int i = 1; i <= 20; i++) {
            random = min + Math.random() * (max - min);
            xPos = 50 * i;                                                                          //50 pixels
            yPos = (float)random;

            this.xPosList.add(xPos);
            this.yPosList.add(yPos);

            path = new Path();                                                                      //Create path
            path.moveTo(xPos, yPos);                                                                //Add values to path
            this.pathList.add(path);                                                                //Add path to pathList
        }
    }

    /***
     * Clears the points list
     */
    private void clearData() {
        this.xPosList.clear();
        this.yPosList.clear();
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                clearData();
                generateData();
                break;
            case MotionEvent.ACTION_UP:
                invalidate();                                                                       //Refresh canvas
                break;
        }
        return true;                                                                                //Activate event
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint p = new Paint();
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        p.setColor(Color.GREEN);
        p.setStrokeWidth(10);
        p.setStyle(Paint.Style.FILL);
        /***
         * Better use 50 by 50 pixels
         */

        float startX = 0;                                                                           //Start graph at bottom left
        float startY = canvas.getHeight();                                                          //Start at the bottom (max height)

        float nextX = 0;
        float nextY = 0;

        for (int i = 0; i < this.xPosList.size(); i++){

            nextX = this.xPosList.get(i);
            //TODO: Find a better function
            nextY = (canvas.getHeight() - this.yPosList.get(i));
            canvas.drawLine(startX, startY, nextX, nextY, p);                                       //Draw segment

            startX = nextX;                                                                         //Save previous X point
            startY = nextY;                                                                         //Save previous Y point
        }

        //TODO: Find a better way to manage this
        cl = (ConstraintLayout) ((ViewGroup)this.getParent());                                      //get parent Layout
        this.stockPriceView = cl.findViewById(R.id.stockPriceText);                                 //access the sibling
        if (this.stockPriceView != null) {
            this.stockPriceView.setText((nextY)+"");                                           //Write number
        }
    }
}

虽然我目前的输出与期望相差不远,但它是不正确的。

看起来您的最大 y 值将是 100 像素,与 1000 像素的最大 x 值相比,这不是很多而且相形见绌。您需要将 y 值转换为 dps 或其他一些缩放值以填充更多视图。

具体来说,在 generateData() 内,x 将设置为 50..1,000 的范围,增量为 50,而 y 值将随机分配 5 到 100 之间的值。在绘图代码中,您使用drawLine() 的这些值以像素作为参数。 x-direction 中的 1,000 像素将使您在大多数设备上保持一定距离(在密度为 3 pixels/dp 的设备上为 333.3 dps),但 y-value 的最大值为 100您在同一台设备上最多 33.3 dps - 即距离的 1/10。

假设您希望 x 值跨越 Plotter 视图的宽度,同样对于 y-axis,这些值应该跨越高度。所以,当 x == 0 时,drawLine() 的 x 值也应为 0。当 x-value 为 1,000 时,drawLine() 的 x 值应为 [=38] 的宽度=]绘图仪视图或

xview = viewWidth * x/1,000

同样,对于 drawLine() 的 y 值:

yview = viewHeight * y/100

将循环绘图更改为如下所示:

float viewWidth = getWidth();
float viewHeight = getHeight();
for (int i = 0; i < this.xPosList.size(); i++) {

    nextX = this.xPosList.get(i);
    nextY = this.yPosList.get(i);
    canvas.drawLine(viewWidth * startX / 1000, viewHeight - (viewHeight * startY / 100),
            viewWidth * nextX / 1000, viewHeight - (viewHeight * nextY / 100), p);                                       //Draw segment

    startX = nextX;                                                                         //Save previous X point
    startY = nextY;                                                                         //Save previous Y point
}

你会得到一个看起来像这样的情节:

这是使用 canvas translate() 方法的另一种方法:

float viewWidth = getWidth();
float viewHeight = getHeight();
canvas.save();
// Flip the canvas vertically.
canvas.scale(1f, -1f, canvas.getWidth() / 2f, canvas.getHeight() / 2f);
for (int i = 0; i < this.xPosList.size(); i++) {

    nextX = this.xPosList.get(i);
    nextY = this.yPosList.get(i);
    canvas.drawLine(viewWidth * startX / 1000, viewHeight * startY / 100,
            viewWidth * nextX / 1000, viewHeight * nextY / 100, p);                                       //Draw segment

    startX = nextX;                                                                         //Save previous X point
    startY = nextY;                                                                         //Save previous Y point
}
canvas.restore();