zoukankan      html  css  js  c++  java
  • Android-自定义折线图

    ------------------------------------------------------------------>>> 简单版 ---- Android自定义控件之折线图

    效果图:

    布局代码:

    <!-- 自定义折线图 -->
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:myswitch="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".upgrade.MainActivity">
    
        <custom.view.upgrade.my_line_chart.MyLineChart
            android:id="@+id/my_line_chart"
            android:layout_width="400dp"
            android:layout_height="300dp"
            android:background="@color/ring_test3"/>
    
    </LinearLayout>

    设置参数相关的代码:

    /**
      * 自定义折线图
      */
    MyLineChart myLineChart = findViewById(R.id.my_line_chart);
    myLineChart.setYItemText(new String[]{"1级", "2级", "3级", "4级", "5级"});
    myLineChart.setXItemText(new String[]{"2001", "2002", "2003", "2004", "2005", "2006", "2007"});

    自定义折线图View:

    package custom.view.upgrade.my_line_chart;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.util.AttributeSet;
    import android.view.View;
    
    public class MyLineChart extends View {
    
        private final String TAG = MyLineChart.class.getSimpleName();
    
        public MyLineChart(Context context, AttributeSet attrs) {
            super(context, attrs);
            initAction();
        }
    
        private Paint paintYItem; // 专门用于画Y轴的画笔
        private Paint paintXItem; // 专门用于话X轴的画笔
    
        private Paint paintLine;  // 线与点共用一个画笔
    
        private Rect rectX = new Rect();
        private Rect rectY = new Rect();
    
        private int w;
        private int h;
    
        private String[] xItemText = {}; // X轴的文字
        private String[] yItemText = {}; // Y轴的文字
    
        /**
         * 给使用者设置X轴的文字集合
         */
        public void setXItemText(String[] xItemText) {
            this.xItemText = xItemText;
        }
    
        /**
         * 给使用者设置Y轴的文字集合
         */
        public void setYItemText(String[] yItemText) {
            this.yItemText = yItemText;
            saveYHeightList = new int[yItemText.length];
        }
    
        private void initAction() {
            paintYItem = new Paint();
            paintYItem.setAntiAlias(true); // 抗锯齿
            paintYItem.setColor(Color.RED);
            paintYItem.setTextSize(40);
    
            paintXItem = new Paint();
            paintXItem.setAntiAlias(true);
            paintXItem.setColor(Color.BLUE);
            paintXItem.setTextSize(40);
    
            paintLine = new Paint();
            paintLine.setAntiAlias(true);
            paintLine.setColor(Color.BLACK);
            paintLine.setTextSize(40);
    
        }
    
        /**
         * 测量自身View控件的宽和高,宽和高得到父控件给子控件测量的高宽等
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    
            // 精确模式
            // MeasureSpec.EXACTLY 当控件设置尺寸的值是多少,控件的宽高就是多少
    
            // 最大模式
            // MeasureSpec.AT_MOST 由父控件给出了最大控件,子控件最大只能这么大,当然也可以比父控件小
    
            if (widthMode == MeasureSpec.EXACTLY) {
                w = widthSize;
            }
    
            if (heightMode == MeasureSpec.EXACTLY) {
                h = heightSize;
            }
    
            if (heightMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.AT_MOST) {
                throw new IllegalArgumentException("参数异常,请输入指定大小,例如:200dp");
            }
    
            setMeasuredDimension(w, h);
        }
    
        private int countYHeight; // 计算记录Y的高度
    
        private int[] saveYHeightList; // 保存Y轴 五级的高度
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            if (yItemText.length == 0 || xItemText.length == 0) {
                throw new IllegalArgumentException("参数异常,设置x轴/y轴集合数据值为空");
            }
    
            // 画Y轴集合数据
            for (int y = 0; y< yItemText.length; y++) {
                countYHeight = (y + 1) * 118;
                canvas.drawText(yItemText[y], 40, countYHeight, paintYItem);
                saveYHeightList[y] = countYHeight;
            }
    
            paintLine.getTextBounds(xItemText[0], 0, xItemText[0].length(), rectX);
            paintLine.getTextBounds(yItemText[0], 0, yItemText[0].length(), rectY);
    
            // 画X轴集合的数据
            for (int x = 0; x <xItemText.length; x++) {
                int xLocation = (x + 1) * 122;
                canvas.drawText(xItemText[x], xLocation, (countYHeight + 80), paintXItem);
    
                // 定义线的 开始 到 结束
                int tempStartY = 0;
                int tempStopY = 0;
                switch (x) {
                    case 0:
                        tempStartY = saveYHeightList[0] - (rectY.height() / 3);
                        tempStopY = saveYHeightList[1] - (rectY.height() / 3);
                        break;
                    case 1:
                        tempStartY = saveYHeightList[1] - (rectY.height() / 3);
                        tempStopY = saveYHeightList[2] - (rectY.height() / 3);
                        break;
                    case 2:
                        tempStartY = saveYHeightList[2] - (rectY.height() / 3);
                        tempStopY = saveYHeightList[3] - (rectY.height() / 3);
                        break;
                    case 3:
                        tempStartY = saveYHeightList[3] - (rectY.height() / 3);
                        tempStopY = saveYHeightList[4] - (rectY.height() / 3);
                        break;
                    case 4:
                        tempStartY = saveYHeightList[4] - (rectY.height() / 3);
                        tempStopY = saveYHeightList[3] - (rectY.height() / 3);
                        break;
                    case 5:
                        tempStartY = saveYHeightList[3] - (rectY.height() / 3);
                        tempStopY  = saveYHeightList[1] - (rectY.height() / 3);
                        break;
                    case 6:
                        tempStartY = saveYHeightList[1] - (rectY.height() / 3);
                        break;
                    default:
                        break;
                }
    
                // 画点,注意:X轴需要与点保持一致的位置,只变化Y轴值的位置
                canvas.drawCircle(xLocation + (rectX.width() / 2),
                        tempStartY,
                        20,
                        paintLine);
    
                // 画线
                if (x < xItemText.length-1) {
                    int xLocationLine = ((x + 1) + 1) * 122;
                    canvas.drawLine(xLocation + (rectX.width() / 2),
                            tempStartY,
                            xLocationLine + (rectX.width() / 2),
                            tempStopY,
                            paintLine);
                }
            }
        }
    }

    ------------------------------------------------------------------>>> 升级版 ---- Android自定义控件之折线图

    效果图:

     

    背景颜色:

    布局代码:

    <!-- 自定义折线图 升级版 -->
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:myswitch="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".upgrade.MainActivity">
    
        <custom.view.upgrade.my_line_chart2.MyLineChart2
            android:id="@+id/my_line_chart"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:background="@color/ring_test3"/>
    
    </LinearLayout>

    自定义折线图绘制类:

    package custom.view.upgrade.my_line_chart2;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    import custom.view.R;
    
    public class MyLineChart2 extends View {
    
        private final String TAG = MyLineChart2.class.getSimpleName();
    
        public MyLineChart2(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView();
        }
    
        private Paint xyLinePaint; // X轴与Y轴的灰色线的画笔
        private final int XY_LINE_SPACE = 120; // X轴Y轴距离左边底边距离间隙值
    
        private String[] yListData = {"7.0", "6.0", "5.0", "4.0", "3.0", "2.0", "1.0", "0.0"};
        private String[] xListData = {"活期", "三个月", "六个月", "一年", "两年", "三年", "四年", "五年"};
    
        // 基准利率的画笔
        private Paint standardRatePaint;
    
        // 真惠存利率
        private Paint trueHuicunReatPaint;
    
        // 记录X轴文字的位置
        private int[] xTextLocation;
    
        // 记录Y轴顶部越界值 与底部越界值
        private int topDemarcation;
        private int bottomeDemarcation;
    
        // 基准利率动态变化的Y轴值
        private int changeY;
        private int changStopY;
    
        // 真惠存利率动态变化的Y轴值
        private int changeYHuicun;
        private int changeStopYHuicun;
    
        // 基准利率动态变化的值
        private int[] changYList = {200, 400, 300, 350, 500, 450, 300};
    
        // 真惠存动态变化的值
        private int[] changYHuicunList = {540, 300, 350, 600, 550, 480, 200};
    
        private Rect rect;
    
        // 专门用于画解释信息文字的画笔
        private Paint textInfoPaint;
    
        private void initView() {
            xyLinePaint = new Paint();
            xyLinePaint.setAntiAlias(true); // 抗锯齿
            xyLinePaint.setColor(getResources().getColor(R.color.xyline));
            xyLinePaint.setTextSize(22);
    
            standardRatePaint = new Paint();
            standardRatePaint.setAntiAlias(true);
            standardRatePaint.setColor(getResources().getColor(R.color.standard_rate));
            standardRatePaint.setStyle(Paint.Style.STROKE);
            standardRatePaint.setStrokeWidth(6);
    
            trueHuicunReatPaint = new Paint();
            trueHuicunReatPaint.setAntiAlias(true);
            trueHuicunReatPaint.setColor(getResources().getColor(R.color.true_huicun));
            trueHuicunReatPaint.setStyle(Paint.Style.STROKE);
            trueHuicunReatPaint.setStrokeWidth(6);
    
            xTextLocation = new int[xListData.length];
            rect = new Rect();
    
            textInfoPaint = new Paint();
            textInfoPaint.setAntiAlias(true);
            textInfoPaint.setColor(getResources().getColor(R.color.xyline));
            textInfoPaint.setTextSize(32);
        }
    
        /**
         * 基准利率七个值
         */
        public void setStandardData(int[] changYList) {
            this.changYList = changYList;
        }
    
        /**
         * 真惠存利率七个值
         */
        public void setTureHuicunData(int[] changYHuicunList) {
            this.changYHuicunList = changYHuicunList;
        }
    
        private int viewWidth; // 当前控件的宽度
        private int viewHeight; // 当前控件的高度
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            // MeasureSpec.EXACTLY 精确模式,指定大小,例如:200dp
            // MeasureSpec.AT_MOST 最大模式,由父控件控制最大,子控件不能比父控件大,当然可以比父控件小
    
            if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
                viewHeight = MeasureSpec.getSize(heightMeasureSpec);
            } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
                throw new IllegalArgumentException("高度指定参数异常,请按照,例如:200dp");
            }
            viewWidth = MeasureSpec.getSize(widthMeasureSpec);
    
            // 这两个值是根据手机屏幕而变化的
            Log.d(TAG, "viewHeight:" + viewHeight + " viewWidth:" + viewWidth);
    
            setMeasuredDimension(viewWidth, viewHeight);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // 画Y轴的箭头符号
            canvas.drawLine(XY_LINE_SPACE, 40, XY_LINE_SPACE + 16, 80, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, 40, XY_LINE_SPACE - 16, 80, xyLinePaint);
    
            // 画Y轴线
            canvas.drawLine(XY_LINE_SPACE, 40, XY_LINE_SPACE, viewHeight - XY_LINE_SPACE, xyLinePaint);
    
            // 画Y轴线中的短线 15根
            int  startStopY = 0;
            for (int y=0; y<15; y++) {
                if (y == 0) {
                    startStopY = 80;
                }
                startStopY += 35;
                canvas.drawLine(XY_LINE_SPACE, startStopY, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            }
            /*canvas.drawLine(XY_LINE_SPACE, startStopY = 100, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);
            canvas.drawLine(XY_LINE_SPACE, startStopY += 35, XY_LINE_SPACE + 20, startStopY, xyLinePaint);*/
    
            // 画Y轴线中的信息 字符串
            int textY = 0;
            for (int y2 = 0; y2 < yListData.length; y2 ++) {
                if (y2 == 0) {
                    textY = 80;
                    topDemarcation = textY;
                    topDemarcation += 60;
                }
                textY += 60;
                bottomeDemarcation = textY;
                canvas.drawText(yListData[y2], XY_LINE_SPACE - 60, textY, xyLinePaint);
            }
    
            // 画X的箭头符号
            canvas.drawLine(viewWidth - (viewWidth / 8), viewHeight - XY_LINE_SPACE, (viewWidth - (viewWidth / 8)) - 40, (viewHeight - XY_LINE_SPACE) - 16, xyLinePaint);
            canvas.drawLine(viewWidth - (viewWidth / 8), viewHeight - XY_LINE_SPACE, (viewWidth - (viewWidth / 8)) - 40, (viewHeight - XY_LINE_SPACE) + 16, xyLinePaint);
    
            // 画X轴线
            canvas.drawLine(XY_LINE_SPACE, viewHeight - XY_LINE_SPACE, viewWidth - (viewWidth / 8), viewHeight - XY_LINE_SPACE, xyLinePaint);
    
            // 画15根X轴线
            int startStopX = 0;
            for (int x = 0; x<14; x ++) {
                if (x == 0) {
                    startStopX = XY_LINE_SPACE + 20;
                }
                startStopX += 50;
                canvas.drawLine(startStopX, viewHeight - XY_LINE_SPACE, startStopX, viewHeight - XY_LINE_SPACE - 20, xyLinePaint);
            }
    
            // 画X轴字符串信息
            int testX = 0;
            for (int x2 = 0; x2 < xListData.length; x2 ++) {
                if (testX == 0) {
                    testX = 110;
                }
                testX += 100;
                xTextLocation[x2] = testX; // 记录X轴方向的文字位置
                canvas.drawText(xListData[x2], testX,viewHeight - XY_LINE_SPACE + 40, xyLinePaint);
            }
    
            // 画七个点,X轴方向位置一直不变,Y轴方向的位置是变化的
            for (int dianXY = 0; dianXY < xListData.length - 1; dianXY ++) {
                standardRatePaint.getTextBounds(xListData[dianXY], 0, xListData[dianXY].length(), rect);
    
                changeY = changYList[dianXY];
                // 如果不等于最后一个,就可以+1
                Log.d(TAG, "dianXY:" + dianXY + " changYList.length:" + changYList.length);
                if (dianXY < changYList.length - 1) {
                    changStopY = changYList[dianXY + 1];
                }
    
                if (changeY > bottomeDemarcation) {
                    changeY = bottomeDemarcation;
                } else if (changeY < topDemarcation) {
                    changeY = topDemarcation;
                }
    
                if (changStopY > bottomeDemarcation) {
                    changStopY = bottomeDemarcation;
                } else if (changStopY < topDemarcation) {
                    changStopY = topDemarcation;
                }
    
                Log.d(TAG, "rect.width():" + rect.width() + " bottomeDemarcation:" + bottomeDemarcation
                        + " topDemarcation:" + topDemarcation + " changeY:" + changeY);
    
                standardRatePaint.getTextBounds(yListData[0], 0, yListData[0].length(), rect);
    
                canvas.drawCircle(xTextLocation[dianXY] + (rect.width()) ,changeY - rect.height(),10, standardRatePaint);
    
                // 画线 开始的X轴与Y轴要增加,结束的X轴与Y轴要减少
                if (dianXY < xListData.length - 2) {
                    changeY -= rect.height();
                    // changStopY += rect.height();
                    changStopY -= rect.height();
    
                    /*changeY += 10;
                    changStopY -= 10;*/
    
                    // 画线,线的走向是根据点走的
                    canvas.drawLine(xTextLocation[dianXY] + (rect.width() + 8),
                            changeY,
                            xTextLocation[dianXY > xListData.length ? xListData.length : dianXY + 1] + (rect.width() - 8),
                            changStopY,
                            standardRatePaint);
                }
    
                /**
                 * 真惠存利率区域
                 */
                changeYHuicun = changYHuicunList[dianXY];
                // 如果不等于最后一个,就可以+1
                Log.d(TAG, "changYHuicunList.length:" + changYHuicunList.length);
                if (dianXY < changYHuicunList.length - 1) {
                    changeStopYHuicun = changYHuicunList[dianXY + 1];
                }
    
                if (changeYHuicun > bottomeDemarcation) {
                    changeYHuicun = bottomeDemarcation;
                } else if (changeYHuicun < topDemarcation) {
                    changeYHuicun = topDemarcation;
                }
    
                if (changeStopYHuicun > bottomeDemarcation) {
                    changeStopYHuicun = bottomeDemarcation;
                } else if (changeStopYHuicun < topDemarcation) {
                    changeStopYHuicun = topDemarcation;
                }
    
                trueHuicunReatPaint.getTextBounds(yListData[0], 0, yListData[0].length(), rect);
    
                canvas.drawCircle(xTextLocation[dianXY] + (rect.width()) ,changeYHuicun - rect.height(),10, trueHuicunReatPaint);
    
                // 画线 开始的X轴与Y轴要增加,结束的X轴与Y轴要减少
                if (dianXY < xListData.length - 2) {
                    changeYHuicun -= rect.height();
                    changeStopYHuicun -= rect.height();
    
                    /*changeY += 10;
                    changStopY -= 10;*/
    
                    // 画线,线的走向是根据点走的
                    canvas.drawLine(xTextLocation[dianXY] + (rect.width() + 8),
                            changeYHuicun,
                            xTextLocation[dianXY > xListData.length ? xListData.length : dianXY + 1] + (rect.width() - 8),
                            changeStopYHuicun,
                            trueHuicunReatPaint);
                }
            }
    
            // 画两个描述:基准利率(%) 真惠存利率(%)
            int heightLineCircle = XY_LINE_SPACE - (XY_LINE_SPACE / 2);
            int radius = 10;
            // 先画一个点
            canvas.drawCircle(xTextLocation[2],  heightLineCircle, radius, standardRatePaint);
            // 画点的左边横线
            canvas.drawLine(xTextLocation[2] - 6, heightLineCircle, xTextLocation[2] - 70, heightLineCircle, standardRatePaint);
            // 画点的右边横线
            canvas.drawLine(xTextLocation[2] + 6,heightLineCircle,xTextLocation[2] + 70, heightLineCircle, standardRatePaint);
            // 画文字
            canvas.drawText("基准利率(%)", xTextLocation[2] - 90, heightLineCircle + 50, textInfoPaint);
    
            // 先画一个点
            canvas.drawCircle(xTextLocation[5], heightLineCircle, radius,trueHuicunReatPaint);
            canvas.drawLine(xTextLocation[5] - 6, heightLineCircle, xTextLocation[5] - 70, heightLineCircle, trueHuicunReatPaint);
            canvas.drawLine(xTextLocation[5] + 6, heightLineCircle, xTextLocation[5] + 70, heightLineCircle, trueHuicunReatPaint);
            canvas.drawText("真惠存利率(%)", xTextLocation[5] - 90, heightLineCircle + 50, textInfoPaint);
        }
    
        /**
         * dp转px
         * @param dp
         * @return
         */
       /* private int dip2px(int dp) {
            float density = getContext().getResources().getDisplayMetrics().density;
            return (int) (dp * density + 0.5);
        }*/
    }
  • 相关阅读:
    推送技术 --SignalR
    软件解耦
    xrBarCode 条形码的密度设置
    Javascript 中方法的重写
    数据库锁
    oracle instr,substr 截取字符串
    循环读取写入表
    Oracle For 循环,字符串拼接,查找
    iis,webservice 启用 acrobat.exe 打印
    iis,webservice 打印
  • 原文地址:https://www.cnblogs.com/android-deli/p/9789710.html
Copyright © 2011-2022 走看看