android动态绘制自定义折线图(不使用第三方)

android动态绘制自定义折线图(不使用第三方),第1张

概述简介很多软件中都会展示统计数据给用户,方式就是各种统计图,柱状图,扇形图,折线图等等。当然现在有很多第三方依赖很容易就可以实现上面说的各种图,此篇的目的就是介绍如何自己动手做一个简单的折线图(注释挺详细,话就不多了)。效果图思路自定义属性数据提供绘制xy轴线,箭头绘 简介

很多软件中都会展示统计数据给用户,方式就是各种统计图,柱状图,扇形图,折线图等等。当然现在有很多第三方依赖很容易就可以实现上面说的各种图,此篇的目的就是介绍如何自己动手做一个简单的折线图(注释挺详细,话就不多了)。

效果图

思路自定义属性数据提供绘制xy轴线,箭头绘制xy轴刻度,文字绘制折线绘制折线上点位圆圈绘制选中d出框和x轴文字选中框x轴滑动自定义属性

attrs中创建需要的属性

  <!-- xy轴线颜色 -->    <attr name="xylinecolor" format="color" />    <!-- xy轴线的宽度 -->    <attr name="xylinewidth" format="dimension" />    <!-- xy轴上的文字颜色 -->    <attr name="xytextcolor" format="color" />    <!-- xy轴上的文字大小 -->    <attr name="xytextsize" format="dimension" />    <!-- 折线的颜色 -->    <attr name="linecolor" format="color" />    <!-- x轴坐标点间距 -->    <attr name="interval" format="dimension" />    <!-- 折线点位信息颜色 -->    <attr name="contentcolor" format="color" />    <!-- 背景颜色 -->    <attr name="bgcolor" format="color" />    <!--滑动后手抬起时,折线是否跟随初速度左右滑动-->    <attr name="isScroll" format="boolean" />    <declare-styleable name="brokenlineVIEw">        <attr name="xylinecolor" />        <attr name="xylinewidth" />        <attr name="xytextcolor" />        <attr name="xytextsize" />        <attr name="linecolor" />        <attr name="contentcolor" />        <attr name="interval" />        <attr name="bgcolor" />        <attr name="isScroll" />    </declare-styleable>

在自定义view类中初始化

/**     * 初始化自定义属性     */    private voID initPar(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.brokenlineVIEw, defStyleAttr, 0);        int count = array.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = array.getIndex(i);            switch (attr) {                case R.styleable.brokenlineVIEw_xylinecolor://xy轴线颜色                    xylinecolor = array.getcolor(attr, xylinecolor);                    break;                case R.styleable.brokenlineVIEw_xylinewidth://xy轴线的宽度                    xylinewidth = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xylinewidth, getResources().getdisplayMetrics()));                    break;                case R.styleable.brokenlineVIEw_xytextcolor://xy轴上的文字颜色                    xytextcolor = array.getcolor(attr, xytextcolor);                    break;                case R.styleable.brokenlineVIEw_xytextsize://xy轴上的文字大小                    xytextsize = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xytextsize, getResources().getdisplayMetrics()));                    break;                case R.styleable.brokenlineVIEw_linecolor://折线的颜色                    linecolor = array.getcolor(attr, linecolor);                    break;                case R.styleable.brokenlineVIEw_contentcolor://折线点位信息颜色                    contentcolor = array.getcolor(attr, contentcolor);                    break;                case R.styleable.brokenlineVIEw_interval://x轴坐标点间距                    interval = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, interval, getResources().getdisplayMetrics()));                    break;                case R.styleable.brokenlineVIEw_bgcolor: //背景颜色                    bgcolor = array.getcolor(attr, bgcolor);                    break;                case R.styleable.brokenlineVIEw_isScroll://滑动后手抬起时,折线是否跟随初速度左右滑动                    isScroll = array.getBoolean(attr, isScroll);                    break;            }        }        array.recycle();    }
数据提供

数据使用三个List分别为x轴的数据,y轴数据,折线数据

    /**     * x轴坐标对应的数据     */    private List<brokenlineVIEw.XValue> xValues = new ArrayList<>();    /**     * y轴坐标对应的数据     */    private List<brokenlineVIEw.YValue> yValues = new ArrayList<>();    /**     * 折线对应的数据     */    private List<brokenlineVIEw.lineValue> lineValues = new ArrayList<>();

 

绘制xy轴线,箭头

设置大小最好是dp转成px。

        //绘制Y轴线        canvas.drawline(xOrigin - xylinewidth / 2, 0, xOrigin - xylinewidth / 2, yOrigin, xyPaint);        //绘制y轴箭头        xyPaint.setStyle(Paint.Style.stroke);        Path path = new Path();        path.moveto(xOrigin - xylinewidth / 2 - dptopx(5), dptopx(12));        path.lineto(xOrigin - xylinewidth / 2, xylinewidth / 2);        path.lineto(xOrigin - xylinewidth / 2 + dptopx(5), dptopx(12));        canvas.drawPath(path, xyPaint);//绘制X轴坐标        canvas.drawline(xOrigin, yOrigin + xylinewidth / 2, wIDth, yOrigin + xylinewidth / 2, xyPaint);        //绘制x轴箭头        xyPaint.setStyle(Paint.Style.stroke);        path = new Path();        //整个X轴的长度        float xLength = xInit + interval * (xValues.size() - 1) + (wIDth - xOrigin) * 0.1f;        if (xLength < wIDth)            xLength = wIDth;        path.moveto(xLength - dptopx(12), yOrigin + xylinewidth / 2 - dptopx(5));        path.lineto(xLength - xylinewidth / 2, yOrigin + xylinewidth / 2);        path.lineto(xLength - dptopx(12), yOrigin + xylinewidth / 2 + dptopx(5));        canvas.drawPath(path, xyPaint);
绘制xy轴刻度,文字
 //绘制y轴刻度        int yLength = (int) (yOrigin * (1 - 0.1f) / (yValues.size() - 1));//y轴上面空出10%,计算出y轴刻度间距        for (int i = 0; i < yValues.size(); i++) {            //绘制Y轴刻度            canvas.drawline(xOrigin, yOrigin - yLength * i + xylinewidth / 2, xOrigin , yOrigin - yLength * i + xylinewidth / 2, xyPaint);            xyTextPaint.setcolor(xytextcolor);            //绘制Y轴文本            String text = yValues.get(i).value;            Rect rect = getTextBounds(text, xyTextPaint);            canvas.drawText(text, 0, text.length(), xOrigin - xylinewidth - dptopx(2) - rect.wIDth(), yOrigin - yLength * i + rect.height() / 2, xyTextPaint);        }      //绘制x轴刻度        for (int i = 0; i < xValues.size(); i++) {            float x = xInit + interval * i;            if (x >= xOrigin) {//只绘制从原点开始的区域                xyTextPaint.setcolor(xytextcolor);                canvas.drawline(x, yOrigin, x, yOrigin , xyPaint);                //绘制X轴文本                String text = xValues.get(i).value;                Rect rect = getTextBounds(text, xyTextPaint);                if (i == selectIndex - 1) {                    //绘制x轴选中文字和框                    xyTextPaint.setStyle(Paint.Style.FILL);                    xyTextPaint.setcolor(contentcolor);                    canvas.drawRoundRect(new RectF(x - xValueRect.wIDth() / 2 - dptopx(3), yOrigin + xylinewidth + dptopx(1), x + xValueRect.wIDth() / 2 + dptopx(3), yOrigin + xylinewidth + dptopx(2) + xValueRect.height() + dptopx(2)), dptopx(2), dptopx(2), xyTextPaint);                    xyTextPaint.setcolor(color.WHITE);                    canvas.drawText(text, 0, text.length(), x - rect.wIDth() / 2, yOrigin + xylinewidth + dptopx(2) + rect.height(), xyTextPaint);                } else {                    canvas.drawText(text, 0, text.length(), x - rect.wIDth() / 2, yOrigin + xylinewidth + dptopx(2) + rect.height(), xyTextPaint);                }            }        }
绘制折线
/**     * 绘制折线     */    private voID drawbrokenline(Canvas canvas) {        linePaint.setStyle(Paint.Style.stroke);        linePaint.setcolor(linecolor);        //绘制折线        Path path = new Path();        float x = xInit + interval * 0;        float y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(0).num / yValues.get(yValues.size() - 1).num);        path.moveto(x, y);        for (int i = 1; i < xValues.size(); i++) {            x = xInit + interval * i;            y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(i).num / yValues.get(yValues.size() - 1).num);            if (x<0)x=0;            if (y<0)y=0;            path.lineto(x, y);        }        canvas.drawPath(path, linePaint);    }
绘制折线上点位圆圈
 /**     * 绘制折线对应的点位圆圈     */    private voID drawbrokenPoint(Canvas canvas) {        float dp2 = dptopx(2);        float dp4 = dptopx(4);        float dp7 = dptopx(7);        //绘制节点对应的原点        for (int i = 0; i < xValues.size(); i++) {            float x = xInit + interval * i;            float y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(i).num / yValues.get(yValues.size() - 1).num);            //绘制选中的点            if (i == selectIndex - 1) {                linePaint.setStyle(Paint.Style.FILL);                linePaint.setcolor(contentcolor);                canvas.drawCircle(x, y, dp7, linePaint);                drawfloatTextBox(canvas, x, y - dp7, lineValues.get(i).value);            }            //绘制折线点            linePaint.setStyle(Paint.Style.FILL);            linePaint.setcolor(color.WHITE);            canvas.drawCircle(x, y, dp2, linePaint);            linePaint.setStyle(Paint.Style.stroke);            linePaint.setcolor(linecolor);            canvas.drawCircle(x, y, dp2, linePaint);        }    }
绘制选中d出框和x轴文字选中框
       if (i == selectIndex - 1) {                    //绘制x轴选中文字和框                    xyTextPaint.setStyle(Paint.Style.FILL);                    xyTextPaint.setcolor(contentcolor);                    canvas.drawRoundRect(new RectF(x - xValueRect.wIDth() / 2 - dptopx(3), yOrigin + xylinewidth + dptopx(1), x + xValueRect.wIDth() / 2 + dptopx(3), yOrigin + xylinewidth + dptopx(2) + xValueRect.height() + dptopx(2)), dptopx(2), dptopx(2), xyTextPaint);                    xyTextPaint.setcolor(color.WHITE);                    canvas.drawText(text, 0, text.length(), x - rect.wIDth() / 2, yOrigin + xylinewidth + dptopx(2) + rect.height(), xyTextPaint);                } 
   if (i == selectIndex - 1) {                linePaint.setStyle(Paint.Style.FILL);                linePaint.setcolor(contentcolor);                canvas.drawCircle(x, y, dp7, linePaint);                drawfloatTextBox(canvas, x, y - dp7, lineValues.get(i).value);            }    /**     * 绘制点位信息d出框     */    private voID drawfloatTextBox(Canvas canvas, float x, float y, String text) {        int dp6 = dptopx(6);        int dp45 = dptopx(45);        Path path = new Path();        path.moveto(x, y);        path.lineto(x - dp6, y - dp6);        path.lineto(x - dp45, y - dp6);        path.lineto(x - dp45, y - dp6 - dp45);        path.lineto(x + dp45, y - dp6 - dp45);        path.lineto(x + dp45, y - dp6);        path.lineto(x + dp6, y - dp6);        path.lineto(x, y);        canvas.drawPath(path, linePaint);        //点位信息文字        linePaint.setcolor(color.WHITE);        linePaint.setTextSize(sptopx(14));        Rect rect = getTextBounds(text + "", linePaint);        canvas.drawText(text + "", x - rect.wIDth() / 2, y - dp6 - (dp45 - rect.height()) / 2, linePaint);    }
x轴滑动
/**     * 滑动x轴     */    private voID scroll() {        if (!isScroll)            return;        final float veLocity = getVeLocity();//得到当前速度        float scrollLength = maxXInit - minXInit;        if (Math.abs(veLocity) < 10000)//最大速度            scrollLength = (maxXInit - minXInit) * Math.abs(veLocity) / 10000;        ValueAnimator animator = ValueAnimator.offloat(0, scrollLength);        animator.setDuration((long) (scrollLength / (maxXInit - minXInit) * 1000));//滑动时间最大1000毫秒        animator.setInterpolator(new DecelerateInterpolator());        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @OverrIDe            public voID onAnimationUpdate(ValueAnimator valueAnimator) {                float lineValues = (float) valueAnimator.getAnimatedValue();                if (veLocity < 0 && xInit > minXInit) {//向左滑动                    if (xInit - lineValues <= minXInit)                        xInit = minXInit;                    else                        xInit = xInit - lineValues;                } else if (veLocity > 0 && xInit < maxXInit) {//向右滑动                    if (xInit + lineValues >= maxXInit)                        xInit = maxXInit;                    else                        xInit = xInit + lineValues;                }                invalIDate();            }        });        animator.addListener(new Animator.AnimatorListener() {            @OverrIDe            public voID onAnimationStart(Animator animator) {                isScrolling = true;//正在滑动            }            @OverrIDe            public voID onAnimationEnd(Animator animator) {                isScrolling = false;            }            @OverrIDe            public voID onAnimationCancel(Animator animator) {                isScrolling = false;            }            @OverrIDe            public voID onAnimationRepeat(Animator animator) {            }        });        animator.start();    }

 

完整类如下
public class brokenlineVIEw extends VIEw {    //xy坐标轴颜色    private int xylinecolor = 0;    /**     * xy坐标轴线的宽度     */    private int xylinewidth = 1;    /**     * xy坐标轴文字颜色     */    private int xytextcolor = 0;    /**     * xy坐标轴文字大小     */    private int xytextsize = sptopx(12);    /**     * 折线图中折线的颜色     */    private int linecolor = 0;    /**     * 折线图中点位信息颜色     */    private int contentcolor = 0;    /**     * x轴各个坐标点水平间距     */    private int interval = dptopx(50);    /**     * 背景颜色     */    private int bgcolor = 0;    /**     * 是否在ACTION_UP时,根据速度进行自滑动,没有要求,建议关闭,过于占用GPU     */    private boolean isScroll = false;    /**     * 绘制XY轴坐标对应的画笔     */    private Paint xyPaint;    /**     * 绘制XY轴的文本对应的画笔     */    private Paint xyTextPaint;    /**     * 画折线对应的画笔     */    private Paint linePaint;    private int wIDth;    private int height;    /**     * x轴的原点坐标     */    private int xOrigin;    /**     * y轴的原点坐标     */    private int yOrigin;    /**     * 第一个点X的坐标     */    private float xInit;    /**     * 第一个点对应的最大Y坐标     */    private float maxXInit;    /**     * 第一个点对应的最小X坐标     */    private float minXInit;    /**     * x轴坐标对应的数据     */    private List<XValue> xValues = new ArrayList<>();    /**     * y轴坐标对应的数据     */    private List<YValue> yValues = new ArrayList<>();    /**     * 折线对应的数据     */    private List<lineValue> lineValues = new ArrayList<>();    /**     * 当前选中点     */    private int selectIndex = 1;    /**     * X轴刻度文本对应的最大矩形,为了选中时,在x轴文本画的框框大小一致     */    private Rect xValueRect;    /**     * 速度检测     */    private VeLocityTracker veLocityTracker;    public brokenlineVIEw(Context context) {        this(context, null);    }    public brokenlineVIEw(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public brokenlineVIEw(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initPar(context, attrs, defStyleAttr);        initPaint();    }    /**     * 初始化画笔     */    private voID initPaint() {        xyPaint = new Paint();        xyPaint.setAntiAlias(true);        xyPaint.setstrokeWIDth(xylinewidth);        xyPaint.setstrokeCap(Paint.Cap.ROUND);        xyPaint.setcolor(xylinecolor);        xyTextPaint = new Paint();        xyTextPaint.setAntiAlias(true);        xyTextPaint.setTextSize(xytextsize);        xyTextPaint.setstrokeCap(Paint.Cap.ROUND);        xyTextPaint.setcolor(xytextcolor);        xyTextPaint.setStyle(Paint.Style.stroke);        linePaint = new Paint();        linePaint.setAntiAlias(true);        linePaint.setstrokeWIDth(xylinewidth);        linePaint.setstrokeCap(Paint.Cap.ROUND);        linePaint.setcolor(linecolor);        linePaint.setStyle(Paint.Style.stroke);    }    /**     * 初始化自定义属性     */    private voID initPar(Context context, AttributeSet attrs, int defStyleAttr) {        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.brokenlineVIEw, defStyleAttr, 0);        int count = array.getIndexCount();        for (int i = 0; i < count; i++) {            int attr = array.getIndex(i);            switch (attr) {                case R.styleable.brokenlineVIEw_xylinecolor://xy轴线颜色                    xylinecolor = array.getcolor(attr, xylinecolor);                    break;                case R.styleable.brokenlineVIEw_xylinewidth://xy轴线的宽度                    xylinewidth = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xylinewidth, getResources().getdisplayMetrics()));                    break;                case R.styleable.brokenlineVIEw_xytextcolor://xy轴上的文字颜色                    xytextcolor = array.getcolor(attr, xytextcolor);                    break;                case R.styleable.brokenlineVIEw_xytextsize://xy轴上的文字大小                    xytextsize = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xytextsize, getResources().getdisplayMetrics()));                    break;                case R.styleable.brokenlineVIEw_linecolor://折线的颜色                    linecolor = array.getcolor(attr, linecolor);                    break;                case R.styleable.brokenlineVIEw_contentcolor://折线点位信息颜色                    contentcolor = array.getcolor(attr, contentcolor);                    break;                case R.styleable.brokenlineVIEw_interval://x轴坐标点间距                    interval = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, interval, getResources().getdisplayMetrics()));                    break;                case R.styleable.brokenlineVIEw_bgcolor: //背景颜色                    bgcolor = array.getcolor(attr, bgcolor);                    break;                case R.styleable.brokenlineVIEw_isScroll://滑动后手抬起时,折线是否跟随初速度左右滑动                    isScroll = array.getBoolean(attr, isScroll);                    break;            }        }        array.recycle();    }    @OverrIDe    protected voID onLayout(boolean changed, int left, int top, int right, int bottom) {        if (changed) {            //得到宽度高度            wIDth = getWIDth();            height = getHeight();            //Y轴字宽度            float textYWdith = getTextBounds("000", xyTextPaint).wIDth();            for (int i = 0; i < yValues.size(); i++) {//求取y轴文本最大的宽度                float temp = getTextBounds(yValues.get(i).value, xyTextPaint).wIDth();                if (temp > textYWdith)                    textYWdith = temp;            }            int dp2 = dptopx(2);            int dp3 = dptopx(3);            xOrigin = (int) (dp2 + textYWdith + dp2 + xylinewidth);//dp2是y轴文本距离左边,以及距离y轴的距离            xValueRect = getTextBounds("000", xyTextPaint);            //X轴字高度            float textxHeight = xValueRect.height();            for (int i = 0; i < xValues.size(); i++) {//求取x轴文本最大的高度                Rect rect = getTextBounds(xValues.get(i).value + "", xyTextPaint);                if (rect.height() > textxHeight)                    textxHeight = rect.height();                if (rect.wIDth() > xValueRect.wIDth())                    xValueRect = rect;            }            yOrigin = (int) (height - dp2 - textxHeight - dp3 - xylinewidth);//dp3是x轴文本距离底边,dp2是x轴文本距离x轴的距离            xInit = interval + xOrigin;            minXInit = wIDth - (wIDth - xOrigin) * 0.1f - interval * (xValues.size() - 1);//减去0.1f是因为最后一个X周刻度距离右边的长度为X轴可见长度的10%            maxXInit = xInit;        }        super.onLayout(changed, left, top, right, bottom);    }    @OverrIDe    protected voID onDraw(Canvas canvas) {//        super.onDraw(canvas);        canvas.drawcolor(bgcolor);        drawXY(canvas);        drawbrokenlineAndPoint(canvas);    }    /**     * 绘制折线和折线点     */    private voID drawbrokenlineAndPoint(Canvas canvas) {        if (xValues.size() <= 0)            return;        //设置显示折线的图层        int layer = canvas.saveLayer(0, 0, wIDth, height, null, Canvas.ALL_SAVE_FLAG);        drawbrokenline(canvas);        drawbrokenPoint(canvas);        // 将折线超出x轴坐标的部分截取掉        linePaint.setStyle(Paint.Style.FILL);        linePaint.setcolor(bgcolor);        linePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));        RectF rectF = new RectF(0, 0, xOrigin, height);        canvas.drawRect(rectF, linePaint);        linePaint.setXfermode(null);        //保存图层        canvas.restoretoCount(layer);    }    /**     * 绘制折线对应的点位圆圈     */    private voID drawbrokenPoint(Canvas canvas) {        float dp2 = dptopx(2);        float dp4 = dptopx(4);        float dp7 = dptopx(7);        //绘制节点对应的原点        for (int i = 0; i < xValues.size(); i++) {            float x = xInit + interval * i;            float y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(i).num / yValues.get(yValues.size() - 1).num);            //绘制折线点            linePaint.setStyle(Paint.Style.FILL);            linePaint.setcolor(color.WHITE);            canvas.drawCircle(x, y, dp2, linePaint);            linePaint.setStyle(Paint.Style.stroke);            linePaint.setcolor(linecolor);            canvas.drawCircle(x, y, dp2, linePaint);            //绘制选中的点            if (i == selectIndex - 1) {                linePaint.setStyle(Paint.Style.FILL);                linePaint.setcolor(contentcolor);                canvas.drawCircle(x, y, dp7, linePaint);                drawfloatTextBox(canvas, x, y - dp7, lineValues.get(i).value);            }        }    }    /**     * 绘制点位信息d出框     */    private voID drawfloatTextBox(Canvas canvas, float x, float y, String text) {        int dp6 = dptopx(6);        int dp45 = dptopx(45);        Path path = new Path();        path.moveto(x, y);        path.lineto(x - dp6, y - dp6);        path.lineto(x - dp45, y - dp6);        path.lineto(x - dp45, y - dp6 - dp45);        path.lineto(x + dp45, y - dp6 - dp45);        path.lineto(x + dp45, y - dp6);        path.lineto(x + dp6, y - dp6);        path.lineto(x, y);        canvas.drawPath(path, linePaint);        //点位信息文字        linePaint.setcolor(color.WHITE);        linePaint.setTextSize(sptopx(14));        Rect rect = getTextBounds(text + "", linePaint);        canvas.drawText(text + "", x - rect.wIDth() / 2, y - dp6 - (dp45 - rect.height()) / 2, linePaint);    }    /**     * 绘制折线     */    private voID drawbrokenline(Canvas canvas) {        linePaint.setStyle(Paint.Style.stroke);        linePaint.setcolor(linecolor);        //绘制折线        Path path = new Path();        float x = xInit + interval * 0;        float y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(0).num / yValues.get(yValues.size() - 1).num);        path.moveto(x, y);        for (int i = 1; i < xValues.size(); i++) {            x = xInit + interval * i;            y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(i).num / yValues.get(yValues.size() - 1).num);            if (x < 0) x = 0;            if (y < 0) y = 0;            path.lineto(x, y);        }        canvas.drawPath(path, linePaint);    }    /**     * 绘制XY坐标     *     * @param canvas     */    private voID drawXY(Canvas canvas) {        //绘制Y轴线        canvas.drawline(xOrigin - xylinewidth / 2, 0, xOrigin - xylinewidth / 2, yOrigin, xyPaint);        //绘制y轴箭头        xyPaint.setStyle(Paint.Style.stroke);        Path path = new Path();        path.moveto(xOrigin - xylinewidth / 2 - dptopx(5), dptopx(12));        path.lineto(xOrigin - xylinewidth / 2, xylinewidth / 2);        path.lineto(xOrigin - xylinewidth / 2 + dptopx(5), dptopx(12));        canvas.drawPath(path, xyPaint);        //绘制y轴刻度        int yLength = (int) (yOrigin * (1 - 0.1f) / (yValues.size() - 1));//y轴上面空出10%,计算出y轴刻度间距        for (int i = 0; i < yValues.size(); i++) {            //绘制Y轴刻度            canvas.drawline(xOrigin, yOrigin - yLength * i + xylinewidth / 2, xOrigin, yOrigin - yLength * i + xylinewidth / 2, xyPaint);            xyTextPaint.setcolor(xytextcolor);            //绘制Y轴文本            String text = yValues.get(i).value;            Rect rect = getTextBounds(text, xyTextPaint);            canvas.drawText(text, 0, text.length(), xOrigin - xylinewidth - dptopx(2) - rect.wIDth(), yOrigin - yLength * i + rect.height() / 2, xyTextPaint);        }        //绘制X轴坐标        canvas.drawline(xOrigin, yOrigin + xylinewidth / 2, wIDth, yOrigin + xylinewidth / 2, xyPaint);        //绘制x轴箭头        xyPaint.setStyle(Paint.Style.stroke);        path = new Path();        //整个X轴的长度        float xLength = xInit + interval * (xValues.size() - 1) + (wIDth - xOrigin) * 0.1f;        if (xLength < wIDth)            xLength = wIDth;        path.moveto(xLength - dptopx(12), yOrigin + xylinewidth / 2 - dptopx(5));        path.lineto(xLength - xylinewidth / 2, yOrigin + xylinewidth / 2);        path.lineto(xLength - dptopx(12), yOrigin + xylinewidth / 2 + dptopx(5));        canvas.drawPath(path, xyPaint);        //绘制x轴刻度        for (int i = 0; i < xValues.size(); i++) {            float x = xInit + interval * i;            if (x >= xOrigin) {//只绘制从原点开始的区域                xyTextPaint.setcolor(xytextcolor);                canvas.drawline(x, yOrigin, x, yOrigin, xyPaint);                //绘制X轴文本                String text = xValues.get(i).value;                Rect rect = getTextBounds(text, xyTextPaint);                if (i == selectIndex - 1) {                    //绘制x轴选中文字和框                    xyTextPaint.setStyle(Paint.Style.FILL);                    xyTextPaint.setcolor(contentcolor);                    canvas.drawRoundRect(new RectF(x - xValueRect.wIDth() / 2 - dptopx(3), yOrigin + xylinewidth + dptopx(1), x + xValueRect.wIDth() / 2 + dptopx(3), yOrigin + xylinewidth + dptopx(2) + xValueRect.height() + dptopx(2)), dptopx(2), dptopx(2), xyTextPaint);                    xyTextPaint.setcolor(color.WHITE);                    canvas.drawText(text, 0, text.length(), x - rect.wIDth() / 2, yOrigin + xylinewidth + dptopx(2) + rect.height(), xyTextPaint);                } else {                    canvas.drawText(text, 0, text.length(), x - rect.wIDth() / 2, yOrigin + xylinewidth + dptopx(2) + rect.height(), xyTextPaint);                }            }        }    }    private float startX;    @OverrIDe    public boolean ontouchEvent(MotionEvent event) {        if (isScrolling)            return super.ontouchEvent(event);        this.getParent().requestdisallowIntercepttouchEvent(true);//当该vIEw获得点击事件,就请求父控件不拦截事件        obtainVeLocityTracker(event);        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                startX = event.getX();                break;            case MotionEvent.ACTION_MOVE:                if (interval * xValues.size() > wIDth - xOrigin) {//当期的宽度不足以呈现全部数据                    float dis = event.getX() - startX;                    startX = event.getX();                    if (xInit + dis < minXInit) {                        xInit = minXInit;                    } else if (xInit + dis > maxXInit) {                        xInit = maxXInit;                    } else {                        xInit = xInit + dis;                    }                    invalIDate();                }                break;            case MotionEvent.ACTION_UP:                clickAction(event);                scroll();                this.getParent().requestdisallowIntercepttouchEvent(false);                recycleVeLocityTracker();                break;            case MotionEvent.ACTION_CANCEL:                this.getParent().requestdisallowIntercepttouchEvent(false);                recycleVeLocityTracker();                break;        }        return true;    }    //是否正在滑动    private boolean isScrolling = false;    /**     * 滑动x轴     */    private voID scroll() {        if (!isScroll)            return;        final float veLocity = getVeLocity();//得到当前速度        float scrollLength = maxXInit - minXInit;        if (Math.abs(veLocity) < 10000)//最大速度            scrollLength = (maxXInit - minXInit) * Math.abs(veLocity) / 10000;        ValueAnimator animator = ValueAnimator.offloat(0, scrollLength);        animator.setDuration((long) (scrollLength / (maxXInit - minXInit) * 1000));//滑动时间最大1000毫秒        animator.setInterpolator(new DecelerateInterpolator());        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @OverrIDe            public voID onAnimationUpdate(ValueAnimator valueAnimator) {                float lineValues = (float) valueAnimator.getAnimatedValue();                if (veLocity < 0 && xInit > minXInit) {//向左滑动                    if (xInit - lineValues <= minXInit)                        xInit = minXInit;                    else                        xInit = xInit - lineValues;                } else if (veLocity > 0 && xInit < maxXInit) {//向右滑动                    if (xInit + lineValues >= maxXInit)                        xInit = maxXInit;                    else                        xInit = xInit + lineValues;                }                invalIDate();            }        });        animator.addListener(new Animator.AnimatorListener() {            @OverrIDe            public voID onAnimationStart(Animator animator) {                isScrolling = true;//正在滑动            }            @OverrIDe            public voID onAnimationEnd(Animator animator) {                isScrolling = false;            }            @OverrIDe            public voID onAnimationCancel(Animator animator) {                isScrolling = false;            }            @OverrIDe            public voID onAnimationRepeat(Animator animator) {            }        });        animator.start();    }    /**     * 获取速度     */    private float getVeLocity() {        if (veLocityTracker != null) {            veLocityTracker.computeCurrentVeLocity(1000);            return veLocityTracker.getXVeLocity();        }        return 0;    }    /**     * 点击X轴坐标或者折线节点     *     * @param event     */    private voID clickAction(MotionEvent event) {        int dp8 = dptopx(8);        float eventX = event.getX();        float eventY = event.getY();        for (int i = 0; i < xValues.size(); i++) {            //节点            float x = xInit + interval * i;            float y = (float) (yOrigin - yOrigin * (1 - 0.1f) * lineValues.get(i).num / yValues.get(yValues.size() - 1).num);            if (eventX >= x - dp8 && eventX <= x + dp8 &&                    eventY >= y - dp8 && eventY <= y + dp8 && selectIndex != i + 1) {//每个节点周围8dp都是可点击区域                selectIndex = i + 1;                invalIDate();                return;            }            //X轴刻度            String text = xValues.get(i).value;            Rect rect = getTextBounds(text, xyTextPaint);            x = xInit + interval * i;            y = yOrigin + xylinewidth + dptopx(2);            if (eventX >= x - rect.wIDth() / 2 - dp8 && eventX <= x + rect.wIDth() + dp8 / 2 &&                    eventY >= y - dp8 && eventY <= y + rect.height() + dp8 && selectIndex != i + 1) {                selectIndex = i + 1;                invalIDate();                return;            }        }    }    /**     * 获取速度跟踪器     *     * @param event     */    private voID obtainVeLocityTracker(MotionEvent event) {        if (!isScroll)            return;        if (veLocityTracker == null) {            veLocityTracker = VeLocityTracker.obtain();        }        veLocityTracker.addMovement(event);    }    /**     * 回收速度跟踪器     */    private voID recycleVeLocityTracker() {        if (veLocityTracker != null) {            veLocityTracker.recycle();            veLocityTracker = null;        }    }    public int getSelectIndex() {        return selectIndex;    }    public voID setSelectIndex(int selectIndex) {        this.selectIndex = selectIndex;        invalIDate();    }    public voID setxValue(List<XValue> xValues) {        this.xValues = xValues;    }    public voID setyValue(List<YValue> yValues) {        this.yValues = yValues;        invalIDate();    }    public voID setValue(List<lineValue> lineValues) {        this.lineValues = lineValues;        invalIDate();    }    public voID setValue(List<lineValue> lineValues, List<XValue> xValues, List<YValue> yValues) {        this.lineValues = lineValues;        this.xValues = xValues;        this.yValues = yValues;        invalIDate();    }    public List<XValue> getxValue() {        return xValues;    }    public List<YValue> getyValue() {        return yValues;    }    public List<lineValue> getValue() {        return lineValues;    }    /**     * 获取丈量文本的矩形     *     * @param text     * @param paint     * @return     */    private Rect getTextBounds(String text, Paint paint) {        Rect rect = new Rect();        paint.getTextBounds(text, 0, text.length(), rect);        return rect;    }    /**     * dp转化成为px     *     * @param dp     * @return     */    private int dptopx(int dp) {        float density = getContext().getResources().getdisplayMetrics().density;        return (int) (dp * density + 0.5f * (dp >= 0 ? 1 : -1));    }    /**     * sp转化为px     *     * @param sp     * @return     */    private int sptopx(int sp) {        float scaledDensity = getContext().getResources().getdisplayMetrics().scaledDensity;        return (int) (scaledDensity * sp + 0.5f * (sp >= 0 ? 1 : -1));    }    public static class XValue {        public double num;        public String value;        public XValue(double num, String value) {            this.num = num;            this.value = value;        }    }    public static class YValue {        public double num;        public String value;        public YValue(double num, String value) {            this.num = num;            this.value = value;        }    }    public static class lineValue {        //具体数值        public double num;        //显示值        public String value;        public lineValue(double num, String value) {            this.num = num;            this.value = value;        }    }}
使用方法

布局里面添加brokenlineVIEw

 <test.com.vIEw.brokenlineVIEw        androID:ID="@+ID/chartvIEw"        androID:layout_wIDth="match_parent"        androID:layout_height="match_parent"        androID:layout_marginBottom="8dp"        app:bgcolor="@color/white"        app:interval="50dp"        app:isScroll="false"        app:layout_constraintBottom_toBottomOf="parent"        app:linecolor="@color/red"        app:contentcolor="@color/red"        app:xylinecolor="#494848"        app:xylinewidth="1.5dp"        app:xytextcolor="#494848"        app:xytextsize="15sp"/>

activity里面数据准备

    private voID initData() {        //模拟折线数据        for (int i = 1; i <= 30; i++){            brokenlineVIEw.XValue xValue = new brokenlineVIEw.XValue(i,i+"号");            xValues.add(xValue);            double value =new BigDecimal(Math.random()*10).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();            brokenlineVIEw.lineValue lineValue = new brokenlineVIEw.lineValue(value,i+"号点位="+value);            lineValues.add(lineValue);        }        for (int i = 0; i <=10; i++) {            brokenlineVIEw.YValue yValue = new brokenlineVIEw.YValue(i,i+"");            yValues.add(yValue);        }        brokenlineVIEw.setValue(lineValues, xValues, yValues);    }    private voID initVIEw() {        brokenlineVIEw = (brokenlineVIEw) findVIEwByID(R.ID.brokenlineVIEw);    }
总结

到此自定义的折线图就介绍完了,具体细节的地方讲的不是很详细望包涵,如果要求不是太高可以直接复制上述类代码拿来使用,要求高的就不适用了,因为只是一个测试的折线图,功能不是很完善,当然也可以在上面基础修改增加自己需要的东西。

点赞收藏分享文章举报

浪克oo发布了40 篇原创文章 · 获赞 70 · 访问量 9万+私信 关注 总结

以上是内存溢出为你收集整理的android动态绘制自定义折线图(不使用第三方)全部内容,希望文章能够帮你解决android动态绘制自定义折线图(不使用第三方)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1067764.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存