1. 程式人生 > >使用Canvas和Paint自己繪製一個折線圖

使用Canvas和Paint自己繪製一個折線圖

主要就是使用了Canvas寫了一個特別簡單的小demo。可以手動點選看每個月份的資料。非常簡單。就是用paint在canvas上畫出來的。

主要內容就是計算左邊價格的位置,下面日期的位置,三根虛線的位置,五個點四根折線加價格標籤的位置。

綠色價格標籤是由一個圓角矩形一個三角形加一個text組成的。可以根據點選的位置在五個點上面顯示。

兩種方式:一種加動畫一種不帶動畫,帶動畫的就是先顯示五個點,然後折線從左往右顯示

動畫的實現是用handler不斷改變需要畫的折線的座標的位置,然後一路畫過去。程式碼如下,需要的可以去github自行下載。喜歡的請star

public class ChartView extends ImageView {

    /**
     * 需要傳入一個ChartBeanList,裡面包含表格上顯示的每個點的資訊,包括時間點和價格
     * 
     */

    public static int height = 300;
    public static double width = 600;

    private int heightReal;// 圖表實際的高度
    private int heightText = 70;// 底部顯示時間的區域
    private int heightTop = 0;// 圖表上面的空白區

    private int multi = 1;// 用來適配各種解析度的機器的,為倍數
    private int radi = 5 * multi;// 圓點的半徑

    private int widthReal;// 圖表實際的寬度
    private int widthText = 50 * multi;// 圖表左邊顯示價格的區域
    private int widthHead = 50;// 價格區域右邊,圖表左邊的空白區
    private int widthFoot = 25 * multi;
    private int miles = 5;// 增長的毫秒數
    private int px = 10;// 每毫秒增加的畫素數
    private boolean hasAnim = false;

    private double x1 = 0;
    private double x2 = 0;
    private double x3 = 0;
    private double x4 = 0;
    private double x5 = 0;

    private PointF p1 = new PointF();
    private PointF p2 = new PointF();
    private PointF p3 = new PointF();
    private PointF p4 = new PointF();
    private PointF p5 = new PointF();
    private float p2x;
    private float p3x;
    private float p4x;
    private float p5x;
    private float p2y;
    private float p3y;
    private float p4y;
    private float p5y;
    boolean p2Init = true;
    boolean isFirstRun = true;
    boolean x1Complete = true;
    boolean x2Complete = false;
    boolean x3Complete = false;
    boolean x4Complete = false;
    boolean x5Complete = false;
    private double min = 0d;
    private double max = 100000d;
    private PointF pic;
    private List<ChartBean> mChartBeanList = new ArrayList<ChartBean>();
    private double price;
    private String unit = "元/斤";
    private MyHandler handler;
    private Paint p;
    private float left;
    private float top;
    private float right;
    private float bottom;
    private int textWidth;

    public ChartView(Context context) {
        super(context);
        init();
    }

    public ChartView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public ChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        handler = new MyHandler();

    }

    public void setData(List<ChartBean> list, String unit, boolean hasAnim) {
        this.mChartBeanList = list;
        this.hasAnim = hasAnim;
        if ("".equals(unit)) {
            this.unit = unit;
        }
        // 4,6,2,3,5
        try {
            x1 = list.get(0).getPrice();
            x2 = list.get(1).getPrice();
            x3 = list.get(2).getPrice();
            x4 = list.get(3).getPrice();
            x5 = list.get(4).getPrice();
        } catch (Exception e) {
            e.printStackTrace();
        }
        max = list.get(0).getPrice();
        for (int i = 0; i < list.size(); i++) {
            if (max < list.get(i).getPrice()) {
                max = list.get(i).getPrice();
            }
        }
    }

    public void setMaxMin(double max, double min) {
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        p = new Paint();
        p.setAntiAlias(true);
        p.setColor(Color.GRAY);
        p.setTypeface(Typeface.DEFAULT);
        Path path = new Path();

        width = canvas.getWidth();
        height = canvas.getHeight();
        multi = (int) Math.ceil(width / 560);
        int width = canvas.getWidth();
        widthText = 50 * multi;// 圖表左邊顯示價格的區域
        widthFoot = 25 * multi;
        radi = 5 * multi;

        heightTop = 0;
        heightReal = height - heightText - heightTop;
        heightTop = heightReal / 4;
        heightReal = height - heightText - heightTop;
        widthReal = ((int) width - widthText - widthHead - widthFoot);

        /** 畫左邊的價格文字 */
        p.setTextSize(17 * multi);
        canvas.drawText(String.format("%.2f", (min + (max - min))), widthText - 35 * multi, heightTop, p);
        canvas.drawText(String.format("%.2f", (min + (max - min) * 2 / 3)), widthText - 35 * multi, heightTop
                + heightReal / 3, p);
        canvas.drawText(String.format("%.2f", (min + (max - min) / 3)), widthText - 35 * multi, heightTop + heightReal
                * 2 / 3, p);
        canvas.drawText(unit, widthText - 35 * multi - 5, heightTop + heightReal + 5, p);
        /** 畫底下的日期文字 */
        try {
            switch (mChartBeanList.size()) {
            case 5:
                canvas.drawText(mChartBeanList.get(4).getDate() + "", widthText + widthReal, 50 + heightTop
                        + heightReal, p);
            case 4:
                canvas.drawText(mChartBeanList.get(3).getDate() + "", widthText + widthReal * 3 / 4, 50 + heightTop
                        + heightReal, p);
            case 3:
                canvas.drawText(mChartBeanList.get(2).getDate() + "", widthText + widthReal / 2, 50 + heightTop
                        + heightReal, p);
            case 2:
                canvas.drawText(mChartBeanList.get(1).getDate() + "", widthText + widthReal / 4, 50 + heightTop
                        + heightReal, p);
            case 1:
                canvas.drawText(mChartBeanList.get(0).getDate() + "", widthText, 50 + heightTop + heightReal, p);
            case 0:
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        p1.x = widthText + widthHead;
        p1.y = (float) (heightTop + getHeight(x1));
        p2.x = widthText + widthHead + widthReal / 4;
        p2.y = (float) (heightTop + getHeight(x2));
        p3.x = widthText + widthHead + widthReal / 2;
        p3.y = (float) (heightTop + getHeight(x3));
        p4.x = widthText + widthHead + widthReal * 3 / 4;
        p4.y = (float) (heightTop + getHeight(x4));
        p5.x = widthText + widthHead + widthReal;
        p5.y = (float) (heightTop + getHeight(x5));
        if (p2Init) {
            /** 初始化pp2 */
            p2x = p1.x;
            p2y = p1.y;
            p2Init = false;
        }

        /** 最底下的粗線 */
        p.setColor(Color.GRAY);
        p.setStrokeWidth(2);
        canvas.drawLine(widthText, heightTop + heightReal, (float) width, heightTop + heightReal, p);

        /** 畫三根虛線 */
        p.setColor(Color.GRAY);
        p.setStrokeWidth(1);
        p.setStyle(Paint.Style.STROKE);
        PathEffect effects = new DashPathEffect(new float[] { 5, 5, 5, 5 }, 1);
        p.setPathEffect(effects);
        path.moveTo(widthText, heightTop + heightReal * 2 / 3);
        path.lineTo(width, heightTop + heightReal * 2 / 3);
        canvas.drawPath(path, p);
        path.moveTo(widthText, heightTop + heightReal / 3);
        path.lineTo(width, heightTop + heightReal / 3);
        canvas.drawPath(path, p);
        path.moveTo(widthText, heightTop);
        path.lineTo(width, heightTop);
        canvas.drawPath(path, p);
        p.setStyle(Paint.Style.FILL);
        p.setColor(getResources().getColor(R.color.green_chart));
        p.setStrokeWidth(3 * multi);
        /** 幾個點連起來的折線 */
        try {
            switch (mChartBeanList.size()) {
            case 0:
                break;
            case 1:
                canvas.drawCircle(p1.x, p1.y, radi, p);
                if (pic == null) {
                    pic = new PointF();
                    pic = p1;
                    price = x1;
                }
                break;
            case 2:
                drawLine(canvas, p);
                canvas.drawCircle(p1.x, p1.y, radi, p);
                canvas.drawCircle(p2.x, p2.y, radi, p);
                if (pic == null) {
                    pic = new PointF();
                    pic = p2;
                    price = x2;
                }
                break;
            case 3:
                drawLine(canvas, p);
                canvas.drawCircle(p1.x, p1.y, radi, p);
                canvas.drawCircle(p2.x, p2.y, radi, p);
                canvas.drawCircle(p3.x, p3.y, radi, p);
                if (pic == null) {
                    pic = new PointF();
                    pic = p3;
                    price = x3;
                }
                break;
            case 4:
                drawLine(canvas, p);
                canvas.drawCircle(p1.x, p1.y, radi, p);
                canvas.drawCircle(p2.x, p2.y, radi, p);
                canvas.drawCircle(p3.x, p3.y, radi, p);
                canvas.drawCircle(p4.x, p4.y, radi, p);
                if (pic == null) {
                    pic = new PointF();
                    pic = p4;
                    price = x4;
                }
                break;
            case 5:
                drawLine(canvas, p);
                canvas.drawCircle(p1.x, p1.y, radi, p);
                canvas.drawCircle(p2.x, p2.y, radi, p);
                canvas.drawCircle(p3.x, p3.y, radi, p);
                canvas.drawCircle(p4.x, p4.y, radi, p);
                canvas.drawCircle(p5.x, p5.y, radi, p);
                if (pic == null) {
                    pic = new PointF();
                    pic = p5;
                    price = x5;
                }
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        /** 畫圓角矩形 */
        if (pic != null) {
            // p = new Paint();
            p.setAntiAlias(true);// 設定畫筆的鋸齒效果
            p.setPathEffect(new PathEffect());
            p.setColor(getResources().getColor(R.color.green_chart_dark));
            textWidth = getTextWidth(p, String.format("%.2f", price));
            left = pic.x - textWidth / 2;
            top = pic.y - 35 * multi;
            right = pic.x + textWidth / 2 + 5;
            bottom = pic.y - 15 * multi;
            RectF oval3 = new RectF(left, top, right, bottom);// 設定個新的長方形
            canvas.drawRoundRect(oval3, 10, 10, p);// 第二個引數是x半徑,第三個引數是y半徑
            path.moveTo(pic.x, pic.y - 5 * multi);
            path.lineTo((left + right) / 2 - 5 * multi, bottom);
            path.lineTo((left + right) / 2 + 5 * multi, bottom);
            path.lineTo(pic.x, pic.y - 5 * multi);
            canvas.drawPath(path, p);
            p.setColor(Color.WHITE);
            p.setTextSize(14 * multi);
            canvas.drawText(String.format("%.2f", price), pic.x - textWidth / 2 + 5, pic.y - 20 * multi, p);
        }

        if (hasAnim) {
            handler.sendEmptyMessageDelayed(0, miles);
        }
    }

    public static int getTextWidth(Paint paint, String str) {
        int iRet = 0;
        if (str != null && str.length() > 0) {
            int len = str.length();
            float[] widths = new float[len];
            paint.getTextWidths(str, widths);
            for (int j = 0; j < len; j++) {
                iRet += (int) Math.ceil(widths[j]);
            }
        }
        return iRet;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_MOVE:
            reFreshPic(event);
            break;
        case MotionEvent.ACTION_DOWN:
            reFreshPic(event);
            break;
        case MotionEvent.ACTION_UP:
            reFreshPic(event);
            break;
        }

        return super.onTouchEvent(event);
    }

    private void reFreshPic(MotionEvent event) {
        float x = event.getX();
        int[] location = new int[2];
        this.getLocationOnScreen(location);
        float offsetX = x - widthText - widthHead;
        if (offsetX < widthReal / 8) {
            pic = p1;
            price = x1;
        } else if (offsetX > widthReal / 8 && offsetX < widthReal * 3 / 8) {
            pic = p2;
            price = x2;
        } else if (offsetX > widthReal * 3 / 8 && offsetX < widthReal * 5 / 8) {
            pic = p3;
            price = x3;
        } else if (offsetX > widthReal * 5 / 8 && offsetX < widthReal * 7 / 8) {
            pic = p4;
            price = x4;
        } else if (offsetX > widthReal * 7 / 8) {
            pic = p5;
            price = x5;
        }
        this.invalidate();
    }

    /**
     * 根據傳入的價格得到需要展示在view中的Y座標
     * 
     * max:最大值 min:最小值 height:圖表的高度
     * 
     * @param x
     * @return
     */
    public double getHeight(double x) {
        if (max - min == 0) {
            return 0;
        } else {
            return (max - x) / (max - min) * heightReal;
        }
    }

    public void drawLine(Canvas canvas, Paint p) {
        switch (mChartBeanList.size()) {
        case 0:
            break;
        case 1:
            break;
        case 2:
            if (hasAnim) {
                if (x1Complete) {
                    canvas.drawLine(p1.x, p1.y, p2x, p2y, p);
                }
            } else {
                canvas.drawLine(p1.x, p1.y, p2.x, p2.y, p);
            }
            break;
        case 3:
            if (hasAnim) {
                if (x1Complete) {
                    canvas.drawLine(p1.x, p1.y, p2x, p2y, p);
                }
                if (x2Complete) {
                    canvas.drawLine(p2x, p2y, p3x, p3y, p);
                }
            } else {
                canvas.drawLine(p1.x, p1.y, p2.x, p2.y, p);
                canvas.drawLine(p2.x, p2.y, p3.x, p3.y, p);
            }
            break;
        case 4:
            if (hasAnim) {
                if (x1Complete) {
                    canvas.drawLine(p1.x, p1.y, p2x, p2y, p);
                }
                if (x2Complete) {
                    canvas.drawLine(p2x, p2y, p3x, p3y, p);
                }
                if (x3Complete) {
                    canvas.drawLine(p3x, p3y, p4x, p4y, p);
                }
            } else {
                canvas.drawLine(p1.x, p1.y, p2.x, p2.y, p);
                canvas.drawLine(p2.x, p2.y, p3.x, p3.y, p);
                canvas.drawLine(p3.x, p3.y, p4.x, p4.y, p);
            }
            break;
        case 5:
            if (hasAnim) {
                if (x1Complete) {
                    canvas.drawLine(p1.x, p1.y, p2x, p2y, p);
                }
                if (x2Complete) {
                    canvas.drawLine(p2x, p2y, p3x, p3y, p);
                }
                if (x3Complete) {
                    canvas.drawLine(p3x, p3y, p4x, p4y, p);
                }
                if (x4Complete) {
                    canvas.drawLine(p4x, p4y, p5x, p5y, p);
                }
            } else {
                canvas.drawLine(p1.x, p1.y, p2.x, p2.y, p);
                canvas.drawLine(p2.x, p2.y, p3.x, p3.y, p);
                canvas.drawLine(p3.x, p3.y, p4.x, p4.y, p);
                canvas.drawLine(p4.x, p4.y, p5.x, p5.y, p);
            }
            break;
        }
    }

    class MyHandler extends Handler {
        public MyHandler() {
        }

        public MyHandler(Looper L) {
            super(L);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (p2x < p2.x) {
                p2x = p2x + px;
                p2y = p2y + (p2.y - p1.y) / (p2.x - p1.x) * px;
                // pp2.x++;
                // pp2.y = pp2.y + (p2.y - p1.y) / (p2.x - p1.x) * 1;
                x1Complete = true;
                ChartView.this.invalidate();
                p3x = p2x;
                System.out.println("p2y--" + p2y);
                p3y = p2y;
            } else if (p3x < p3.x) {
                p3x = p3x + px;
                p3y = p3y + (p3.y - p2.y) / (p3.x - p2.x) * px;
                System.out.println("2");
                // pp3.x = pp3.x + 1;
                // pp3.y = pp3.y + (p3.y - p2.y) / (p3.x - p2.x) * 1;
                ChartView.this.invalidate();
                x2Complete = true;
                p4x = p3x;
                p4y = p3y;
            } else if (p4x < p4.x) {
                System.out.println("3");
                p4x = p4x + px;
                p4y = p4y + (p4.y - p3.y) / (p4.x - p3.x) * px;
                // pp4.x = pp4.x + 1;
                // pp4.y = pp4.y + (p4.y - p3.y) / (p4.x - p3.x) * 1;
                ChartView.this.invalidate();
                x3Complete = true;
                p5x = p4x;
                p5y = p4y;
            } else if (p5x < p5.x) {
                p5x = p5x + px;
                System.out.println("4");
                p5y = p5y + (p5.y - p4.y) / (p5.x - p4.x) * px;
                // pp5.x = pp5.x + 1;
                // pp5.y = pp5.y + (p5.y - p4.y) / (p5.x - p4.x) * 1;
                ChartView.this.invalidate();
                x4Complete = true;
            } else {
                System.out.println("return");
                return;
            }
        }
    }
}


https://github.com/yocn/chartView​