android 折线统计图

本文分享了一段自定义Android折线图的代码实现,包括XML布局和LineChartView类的详细解释。代码展示了如何设置X轴和Y轴的数据,以及如何处理数据同步问题。在实际应用中,由于数据可能在折线图绘制完成前未返回,导致错误。解决方案是预先获取所需数据再进行绘制。此外,文章提到了在X轴和Y轴赋值时的数据来源问题,建议在上一页面获取数据后再进行绘制操作。
摘要由CSDN通过智能技术生成

近期,需要做一个折线统计图,来显示相关数据,下面是代码,分享给大家:

xml视图文件:在哪里使用就加进去即可

<HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
            
//			LineChartView为自己本地路径
        <view.linechart.LineChartView
            android:id="@+id/lineChart"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            linchart:lineColor="@color/color_d4"
            linchart:dividerCount="5"
            linchart:xInterval="70dp"
            linchart:leftInterval="20dp"
            linchart:bottomInterval="20dp"
            linchart:topInterval="40dp"
            linchart:yAxisFontSize="14sp"/>
        </HorizontalScrollView>

因为要做的数据格式,X轴的数据较多,所以使用了一个横向滚动(有兴趣的朋友可以研究研究负数及竖向滚动)

LineChartView类:

public class LineChartView extends View{
    private static final String TAG = "LineChartView";

    //Y轴  每个刻度的间距间距
    private int myInterval;
    //X轴  每个刻度的间距间距
    private int mxInterval;
    //Y轴距离view长度
    private int mLeftInterval;
    //X轴距离view长度
    private int mBottomInterval;
    //X轴距离view顶部长度
    private int mTopInterval;
    //Y轴字体的大小
    private float mYAxisFontSize;
    //View 的宽和高
    private int mWidth, mHeight;
    //线的颜色
    private int mLineColor;
    //线条的宽度
    private float mStrokeWidth = 4.0f;
    //X轴的文字
    private ArrayList<String> mXAxis;
    //点 (温度)
    private ArrayList<Integer> mYAxis;
    //纵轴最大值
    private int maxYValue;
    //纵轴分割数量
    private int dividerCount;
    //画坐标线的轴
    private Paint axisPaint;
    //画X轴文字
    private Paint axisTextPaint;
    //连接线条
    private Paint linePaint;
    //小圆点内环
    private Paint innerCirclePaint;
    //小圆点中间环
    private Paint middleCiclePaint;
    //小圆点外环
    private Paint outterCiclePaint;
    //折线路径
    private Path mpolylinePath;
    //小圆点内环半径
    private int innerCircleRadius;
    //小圆点中间环半径
    private int middleRadius;
    //小圆点外环半径
    private int outerRadius;

    public LineChartView(Context context) {
        this(context, null);
        init(context);
    }

    public LineChartView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        init(context);
    }

    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.parseColor("#fbaa56"));
                    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();
    }

    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(Color.parseColor("#fbaa56"));
        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.parseColor("#fbaa56"));
        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.parseColor("#fbaa56"));
        outterCiclePaint.setStrokeWidth(UtilApp.dip2px(context,2));

        // 折线路径
        mpolylinePath = new Path();

        //小圆点内环半径
        innerCircleRadius = UtilApp.dip2px(context,3);
        //小圆点中间环半径
        middleRadius = UtilApp.dip2px(context,4);
        //小圆点外环半径
        outerRadius = UtilApp.dip2px(context,6);
    }

    @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;
    }

    @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);
    }

    @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(Color.parseColor("#fbaa56")); // 设置坐标值的颜色
        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);
            }
        }

    }

    /**
     * 设置Y轴文字
     * @param yItem
     */
    public void setYItem(ArrayList<Integer> yItem){
        mYAxis = yItem;
    }

    /**
     * 设置X轴文字
     * @param xItem
     */
    public void setXItem(ArrayList xItem){
        mXAxis = xItem;
    }


    public void setMaxYValue(int maxYValue) {
        this.maxYValue = maxYValue;
    }
}

类中所需的Util :

public class UtilApp {
    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
    /**
     *  获取屏幕的宽度
     */
    public static int getScreenWidth(Context context) {
        WindowManager manager = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = manager.getDefaultDisplay();
        return display.getWidth();
    }
    /**
     * 获取屏幕的高度
     */
    public static int getScreenHeight(Context context) {
        WindowManager manager = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        Display display = manager.getDefaultDisplay();
        return display.getHeight();
    }

}

然后在需要使用折线图的页面中进行赋值:

protected void initData(Bundle savedInstanceState) {
    private ArrayList<String> xdata;        // X轴数据
    private ArrayList<Integer> ydatas;		// Y轴数据
    
//		xdata和ydatas是和Y轴数据,这里现在是由上一个页面出入,也可以自定义
        xdata = getIntent().getStringArrayListExtra("xdata");
        ydatas = getIntent().getIntegerArrayListExtra("ydatas");
        lineChart = (LineChartView) findViewById(R.id.lineChart);
//		   X轴
        ArrayList xItemArray = new ArrayList();
        for (int i = 0; i < xdata.size(); i++) {
            xItemArray.add(xdata.get(i));
        }
//        Y轴
        ArrayList<Integer> yItemArray = new ArrayList<>();
        for (int i = 0; i < ydatas.size(); i++) {
            yItemArray.add(ydatas.get(i));
        }
        int yMax = Collections.max(ydatas);
        lineChart.setXItem(xItemArray);
        lineChart.setYItem(yItemArray);
        lineChart.setMaxYValue(yMax);
    }

以上,就可以实现折线图啦!!!

但是,在使用过程中,也发现了一个问题:当我们在对X轴及Y轴赋值的时候,如果数据不是已经给定好的,会报错!
例如,我当时给X轴及Y轴赋值的时候,最初采用的是网络请求获取数据然后赋值到X轴及Y轴中,这就可能会出现折线图绘制完成的时候X轴及Y轴的数据还没有返回,所以会报错!!!
所以,我再给X轴及Y轴赋值的时候,先在上一个页面中获取所需的值,然后在加载折线图的时候可以直接拿到X轴及Y轴的数据。

以上,如有不同见解,请评论区留言!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kstar_Ming_闫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值