美文网首页
Android自定义折线图

Android自定义折线图

作者: maiduoduo | 来源:发表于2020-06-02 13:49 被阅读0次

    效果图:

    image image

    设计图标注:

    image

    1.自定义折线图LineChartView的属性,可以参照上面给出的标注图:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="LineChartView">
            <attr name="lineColor" format="color"></attr><!--折线的颜色-->
            <attr name="dividerCount" format="integer"></attr><!--纵轴分割数量-->
            <attr name="xInterval" format="dimension"></attr><!--X轴每个刻度的间距间距-->
            <attr name="leftInterval" format="dimension"></attr><!-- Y轴距离view长度-->
            <attr name="bottomInterval" format="dimension"></attr><!--X轴距离view底部的高度-->
            <attr name="topInterval" format="dimension"></attr><!--X轴距离view顶部长度-->
            <attr name="yAxisFontSize" format="dimension"></attr><!--Y轴字体的大小-->
        </declare-styleable>
    </resources>
    
    

    2.在布局文件使用自定义的属性值画图,自定义一个LineChartView嵌套在HorizontalScrollView下面,让LineChartView可以左右滑动;

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:linchart="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical">
        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="true"
            android:scrollbars="none"
            android:layout_marginTop="50dp">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <com.aotuman.linechart.LineChartView
                    android:id="@+id/lineChart"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    linchart:lineColor="@color/colorLine"
                    linchart:dividerCount="5"
                    linchart:xInterval="70dp"
                    linchart:leftInterval="20dp"
                    linchart:bottomInterval="20dp"
                    linchart:topInterval="40dp"
                    linchart:yAxisFontSize="14sp"/>
            </LinearLayout>
        </HorizontalScrollView>
    
    </LinearLayout>
    
    

    3.看一下LineChartView的代码,在构造函数里获取自定义的属性值:

    public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            /**
             * 获得我们所定义的自定义样式属性
             */
            TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineChartView, defStyleAttr, 0);
            int n = a.getIndexCount();
            for (int i = 0; i < n; i++){
                int index = a.getIndex(i);
                switch (index)
                {
                    // 折线颜色
                    case R.styleable.LineChartView_lineColor:
                        mLineColor = a.getColor(index, Color.BLACK);
                        break;
                    // X轴每个刻度的间距间距
                    case R.styleable.LineChartView_dividerCount:
                        dividerCount = a.getInt(index, 5);
                        break;
                    // X轴每个刻度的间距间距
                    case R.styleable.LineChartView_xInterval:
                        mxInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
                        break;
                    // Y轴距离view长度
                    case R.styleable.LineChartView_leftInterval:
                        mLeftInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
                        break;
                    // X轴距离view底部的高度
                    case R.styleable.LineChartView_bottomInterval:
                        mBottomInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
                        break;
                    // X轴距离view顶部长度
                    case R.styleable.LineChartView_topInterval:
                        mTopInterval = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
                        break;
                    // Y轴字体的大小
                    case R.styleable.LineChartView_yAxisFontSize:
                        // 默认设置为16sp,TypeValue也可以把sp转化为px
                        mYAxisFontSize = a.getDimensionPixelSize(index, (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                        break;
                }
    
            }
            a.recycle();
        }
    
    

    4.然后在构造函数调用init()方法,初始化各种画笔,注意一点的是,折线与折线之间的交点,为了达到线与圆之间有间隙,圆心有空隙,我是用三个画笔来画这圆的.

    private void init(Context context){
            // 画坐标线的轴
            axisPaint = new Paint();
            axisPaint.setTextSize(mYAxisFontSize);
            axisPaint.setColor(Color.parseColor("#D9D9D9"));
    
            // 画X轴文字
            axisTextPaint = new Paint();
            axisTextPaint.setTextSize(mYAxisFontSize);
            axisTextPaint.setColor(Color.parseColor("#878787"));
    
            // 连接线条
            linePaint = new Paint();
            linePaint.setColor(mLineColor);
            linePaint.setAntiAlias(true);
            linePaint.setStyle(Paint.Style.STROKE);
            linePaint.setStrokeWidth(mStrokeWidth);
    
            // 小圆点内环
            innerCirclePaint = new Paint();
            innerCirclePaint.setStyle(Paint.Style.FILL);
            innerCirclePaint.setAntiAlias(true);
            innerCirclePaint.setColor(Color.WHITE);
            innerCirclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
    
            // 小圆点中间环
            middleCiclePaint = new Paint();
            middleCiclePaint.setStyle(Paint.Style.STROKE);
            middleCiclePaint.setAntiAlias(true);
            middleCiclePaint.setColor(mLineColor);
            middleCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
    
            // 小圆点外环
            outterCiclePaint = new Paint();
            outterCiclePaint.setStyle(Paint.Style.STROKE);
            outterCiclePaint.setAntiAlias(true);
            outterCiclePaint.setColor(Color.WHITE);
            outterCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));
    
            // 折线路径
            mpolylinePath = new Path();
    
            //小圆点内环半径
            innerCircleRadius = UtilApp.dip2px(context,3);
            //小圆点中间环半径
            middleRadius = UtilApp.dip2px(context,4);
            //小圆点外环半径
            outerRadius = UtilApp.dip2px(context,6);
        }
    
    

    5.在onMeasure()方法根据传入X轴值的数量算出折线图的宽( mWidth = mxInterval(mXAxis.size()-1) + mLeftInterval2;),折线图的高根据XML的定义好的高度获取.

     @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            Log.d(TAG,"widthSize:"+widthSize+",heightSize:"+heightSize );
            mHeight =heightSize;
            if(mXAxis == null){
                Log.d(TAG,"mWidth:"+mWidth+",mHeight:"+mHeight +"mXAxis:"+mXAxis);
                return;
            }
            //宽度通过数组长度计算
            mWidth = mxInterval*(mXAxis.size()-1) + mLeftInterval*2;
            setMeasuredDimension(mWidth, mHeight);
        }
    
    

    6.在onLayout()方法根据要划分Y轴的数量,算出Y轴每个刻度之间的数量值:

     @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            myInterval = (getHeight()-mBottomInterval-mTopInterval)/dividerCount;
        }
    
    

    7.在onDraw()方法分别画X,Y坐标轴,折线和交点.

     @Override
        protected void onDraw(Canvas canvas) {
            if(mXAxis.size() ==0 || mYAxis.size()==0){
                Log.e(TAG,"数据异常");
                return;
            }
    
            // 画横线
            for(int i = 0;i <= dividerCount;i++){
                canvas.drawLine(mLeftInterval, mHeight - mBottomInterval - i * myInterval, (mXAxis.size()-1)*mxInterval+ mLeftInterval,
                        mHeight - mBottomInterval - myInterval * i, axisPaint);
            }
    
            // x轴的刻度集合
            int[] xPoints = new int[mXAxis.size()];
            for (int i = 0; i < mXAxis.size(); i++) {
                float xTextWidth = axisPaint.measureText(mXAxis.get(i))/2 ; //文字宽度一半
                float xfloat  = i * mxInterval + mLeftInterval - xTextWidth;
                // 画X轴的文字
                canvas.drawText(mXAxis.get(i), xfloat, mHeight - mBottomInterval + mYAxisFontSize, axisTextPaint);
                xPoints[i] = (int) (xfloat+xTextWidth);
    
                // 画竖线
                float xvfloat =  i * mxInterval + mLeftInterval;
                canvas.drawLine(xvfloat,mHeight - mBottomInterval, xvfloat,
                        mHeight - mBottomInterval - myInterval*dividerCount, axisPaint);
            }
    
            /**
             * 画轨迹
             */
            int y = myInterval * (dividerCount - 1); // 只拿纵轴的dividerCount-1/dividerCount画图
            axisPaint.setColor(mLineColor); // 设置坐标值的颜色
            for (int i = 0;i<mYAxis.size();i++){
                int h = mHeight - (mBottomInterval + y * mYAxis.get(i)/ maxYValue);
                float textWidth = axisPaint.measureText(String.valueOf(mYAxis.get(i)))/2 ; //文字宽度一半
                if (i==0){
                    mpolylinePath.moveTo(mLeftInterval,h);
                    canvas.drawText(mYAxis.get(i) + "", mLeftInterval - textWidth, h - mYAxisFontSize, axisPaint);
                }else{
                    mpolylinePath.lineTo(mLeftInterval + i*mxInterval,h);
                    canvas.drawText(mYAxis.get(i) + "", mLeftInterval+i*mxInterval- textWidth, h - mYAxisFontSize, axisPaint);
                }
            }
            canvas.drawPath(mpolylinePath,linePaint);
    
            /**
             * 画小圆圈
             */
            for (int i = 0;i<mYAxis.size();i++){
                int h = mHeight - (mBottomInterval + y * mYAxis.get(i)/ maxYValue);
                if (i==0){
                    canvas.drawCircle(mLeftInterval,h,innerCircleRadius,innerCirclePaint);
                    canvas.drawCircle(mLeftInterval,h,middleRadius,middleCiclePaint);
                    canvas.drawCircle(mLeftInterval,h,outerRadius,outterCiclePaint);
                }else{
                    canvas.drawCircle(mLeftInterval + i*mxInterval,h,innerCircleRadius,innerCirclePaint);
                    canvas.drawCircle(mLeftInterval + i*mxInterval,h,middleRadius,middleCiclePaint);
                    canvas.drawCircle(mLeftInterval + i*mxInterval,h,outerRadius,outterCiclePaint);
                }
            }
    
        }
    
    

    8.在MainActivity设置好X轴和Y轴的值,并求出Y轴的最大值.

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mLineChartView = (LineChartView) findViewById(R.id.lineChart);
            //X轴
            String[] xItem = {"4/1","4/2","4/3","4/4","4/5","4/6","4/7","4/8","4/9","4/10","4/11","4/12",
                    "4/13","4/14","4/15","4/16","4/17","4/18","4/19","4/20","4/21","4/22","4/23","4/24"
                    ,"4/25","4/26","4/27","4/28","4/29","4/30"};
            ArrayList xItemArray = new ArrayList();
            for (int i = 0; i < xItem.length; i++) {
                xItemArray.add(xItem[i]);
            }
            //Y轴
            int[] yItem = {3,7,19,7,20,19,27,8,18,19,21,20,19,20,8,18,19,21,20,22,21,24,26,24,20,22,21,24,26,24};
            ArrayList<Integer> yItemArray = new ArrayList<>();
            for (int i = 0; i < yItem.length; i++) {
                yItemArray.add(yItem[i]);
            }
            int yMax = findMax(yItem);
            mLineChartView.setXItem(xItemArray);
            mLineChartView.setYItem(yItemArray);
            mLineChartView.setMaxYValue(yMax);
    
        }
    
    

    代码地址:https://github.com/michael19900101/LineChart.git

    转载自:
    作者:mcaotuman
    链接:https://www.jianshu.com/p/8679ba7f0f0f

    相关文章

      网友评论

          本文标题:Android自定义折线图

          本文链接:https://www.haomeiwen.com/subject/ogjuzhtx.html