第81篇 Android Studio实现油耗记录App加油耗曲线(一) LineView类

1.说明

这是基于之前的油耗记录App实现的,数据和数据存储,以及相关资源都在前面的文章里:
油耗记录第一篇
添加油耗曲线,要自己自定义一个view类,然后在界面上画就行。

在添加LineView的代码之前,先加上这个文件(如果复制粘贴的话):

arrts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LineView">
        <attr name="viewMargin" format="dimension" />
        <attr name="lineColor" format="color" />
        <attr name="shadowColor" format="color" />
        <attr name="lineTextSize" format="dimension" />
        <attr name="lineTextColor" format="color" />
    </declare-styleable>
</resources>

2.LineView

大佬文章的链接
这是在别人的基础上修改成自己想要的。
所有东西都在代码里面注释吧。

2.1.变量

private int mViewMargin;//View起点距离顶部和底部的距离
private int mLineColor;//网格线的颜色
private int mShadowColor;//阴影部分的颜色
private int mTextSize;//字体大小
private int mTextColor;//字体颜色
private List<String> mYList;//纵坐标刻度
private List<String> mXList;//横坐标刻度

private List<String> mallOilConsumption;//数据
double mallAverageOil;//总的平均油耗
double mallSpent;//总费用
double mallOilCount;//总加油升数

2.2.方法

那些set和get函数方法就不用说了,说一说关键的函数。

2.2.1.主要构造函数

进行这个变量相关信息的初始化。当然这个可以不怎么注意。

@SuppressLint("NonConstantResourceId")
    public LineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineView, defStyleAttr, 0);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int index = array.getIndex(i);
            switch (index) {
                case R.styleable.LineView_viewMargin:
                    mViewMargin = array.getDimensionPixelSize(index, (int) TypedValue.
                            applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.LineView_lineColor:
                    mLineColor = array.getColor(index, Color.BLACK);
                    break;
                case R.styleable.LineView_shadowColor:
                    mShadowColor = array.getColor(index, Color.BLACK);
                    break;
                case R.styleable.LineView_lineTextColor:
                    mTextColor = array.getColor(index, Color.BLACK);
                    break;
                case R.styleable.LineView_lineTextSize:
                    mTextSize = array.getDimensionPixelSize(index, (int) TypedValue.
                            applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
                    break;
            }
        }
        array.recycle();
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(mLineColor);
        mPaint.setTextSize(mTextSize);
        mPaint.setAntiAlias(true);                    //取消锯齿
        mPaint.setStyle(Paint.Style.STROKE);          //设置画笔为空心
        mMarginLeft = (mViewMargin * 2);              //设置左边的偏移距离
        mPaint.setStrokeWidth(2);
    }

2.2.2.最重要的函数

所有的绘图都在这里。就是说如果你想画什么东西,想添加什么东西,都要经过这个函数处理,不然是没有效果的,比如加上曲线的名称,或者添加图片,添加什么颜色等等。其他相关的不用说了,直接代码里看就行了。

 @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeight == 0) {
            mHeight = getHeight() - mViewMargin * 2;
        }
        drawLine(canvas); //画网格
        drawXScale(canvas);//画x轴
        drawYScale(canvas);//画Y轴
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mTextColor);
        mPaint.setStrokeWidth(3);
        mPaint.setAntiAlias(true);
        mListPoint = getPointList();
        drawLineView(canvas);
        Path path = new Path();
        if(!mListPoint.isEmpty()){
            path.moveTo(mListPoint.get(0).x, mListPoint.get(0).y);
            for (int i = 1; i < mallOilConsumption.size() && i < 16; i++) {
                path.lineTo(mListPoint.get(i).x, mListPoint.get(i).y);
            }

            int index = mListPoint.size() - 1;
            path.lineTo(mListPoint.get(index).x, getHeight() - mViewMargin);
            path.lineTo(mListPoint.get(0).x, getHeight() - mViewMargin);
        }
        path.close();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mShadowColor);
        canvas.drawPath(path, mPaint);
    }

2.2.3.数据更新

private List<String> mallOilConsumption;//数据

注意这个变量,这是存储油耗数据的,我们可以在外面添加、删除或者更新其中的数据,然后通过重新设置函数set,来改变当前的数据,在改变数据的同时重绘界面。

public void setMallOilConsumption(List<String> mallOilConsumption) {
    this.mallOilConsumption = mallOilConsumption;
    this.postInvalidate();//重绘界面
}

2.2.4.确定曲线点数和限定范围

因为屏幕太小,所以就只显示15个点,即最近十五次的加油记录,所以在获取点数的时候要设置范围,第一个点是0,0点,不管有没有数据都加进去的,然后限定最多只加15个点。Point是内部类,只管添加就行。

private List<Point> getPointList() {
   List<Point> mList = new ArrayList<>();
    float height = getHeight() - mViewMargin * 2;

    int i = 0;
    for(int j = mallOilConsumption.size() - 1;i < 16 && j >= 0;i++) {
        Point point = new Point();
        if (i == 0) {
            point.x = mMarginLeft;
            point.y = getHeight() - mViewMargin;
        }
        else {
            point.x = mMarginLeft + i * (getWidth() / 17);
            point.y = mViewMargin + height * (1.0f - Float.parseFloat(mallOilConsumption.get(j)) / 2.5f );
            j--;
        }
        mList.add(point);
    }

    return mList;
}

private static class Point {

    public float x;
    public float y;

    public Point() {

    }
}

3.代码

package com.example.listadapter;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;

public class LineView extends View {
    private int mViewMargin;//View起点距离顶部和底部的距离
    private int mLineColor;//网格线的颜色
    private int mShadowColor;//阴影部分的颜色
    private int mTextSize;//字体大小
    private int mTextColor;//字体颜色
    private List<String> mYList;//纵坐标刻度
    private List<String> mXList;//横坐标刻度

    private List<String> mallOilConsumption;//数据
    double mallAverageOil;//总的平均油耗
    double mallSpent;//总费用
    double mallOilCount;//总加油升数

    public double getMallAverageOil() {
        return mallAverageOil;
    }

    public void setMallAverageOil(double mallAverageOil) {
        this.mallAverageOil = mallAverageOil;
    }

    public double getMallSpent() {
        return mallSpent;
    }

    public void setMallSpent(double mallSpent) {
        this.mallSpent = mallSpent;
    }

    public double getMallOilCount() {
        return mallOilCount;
    }

    public void setMallOilCount(double mallOilCount) {
        this.mallOilCount = mallOilCount;
    }

    private int mHeight;//网格线的高度
    private float mMarginLeft;//网格线距离左边的距离

    private List<Point> mListPoint;

    public void setMallOilConsumption(List<String> mallOilConsumption) {
        this.mallOilConsumption = mallOilConsumption;
        this.postInvalidate();
    }

    private Paint mPaint;

    /*构造函数*/
    public LineView(Context context) {
        this(context, null);
    }

    public LineView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @SuppressLint("NonConstantResourceId")
    public LineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineView, defStyleAttr, 0);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int index = array.getIndex(i);
            switch (index) {
                case R.styleable.LineView_viewMargin:
                    mViewMargin = array.getDimensionPixelSize(index, (int) TypedValue.
                            applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.LineView_lineColor:
                    mLineColor = array.getColor(index, Color.BLACK);
                    break;
                case R.styleable.LineView_shadowColor:
                    mShadowColor = array.getColor(index, Color.BLACK);
                    break;
                case R.styleable.LineView_lineTextColor:
                    mTextColor = array.getColor(index, Color.BLACK);
                    break;
                case R.styleable.LineView_lineTextSize:
                    mTextSize = array.getDimensionPixelSize(index, (int) TypedValue.
                            applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
                    break;
            }
        }
        array.recycle();
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(mLineColor);
        mPaint.setTextSize(mTextSize);
        mPaint.setAntiAlias(true);                    //取消锯齿
        mPaint.setStyle(Paint.Style.STROKE);          //设置画笔为空心
        mMarginLeft = (mViewMargin * 2);              //设置左边的偏移距离
        mPaint.setStrokeWidth(2);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeight == 0) {
            mHeight = getHeight() - mViewMargin * 2;
        }
        drawLine(canvas); //画网格
        drawXScale(canvas);//画x轴
        drawYScale(canvas);//画Y轴
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(mTextColor);
        mPaint.setStrokeWidth(3);
        mPaint.setAntiAlias(true);
        mListPoint = getPointList();
        drawLineView(canvas);
        Path path = new Path();
        if(!mListPoint.isEmpty()){
            path.moveTo(mListPoint.get(0).x, mListPoint.get(0).y);
            for (int i = 1; i < mallOilConsumption.size() && i < 16; i++) {
                path.lineTo(mListPoint.get(i).x, mListPoint.get(i).y);
            }

            int index = mListPoint.size() - 1;
            path.lineTo(mListPoint.get(index).x, getHeight() - mViewMargin);
            path.lineTo(mListPoint.get(0).x, getHeight() - mViewMargin);
        }
        path.close();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mShadowColor);
        canvas.drawPath(path, mPaint);
    }

    //绘制网格线
    private void drawLine(Canvas canvas) {
        //左边第一条竖线
        canvas.drawLine(mMarginLeft, mViewMargin + 2f * ((getHeight() - mViewMargin * 2) / 5),
                mMarginLeft, getHeight() - mViewMargin, mPaint);
        //4条水平的横线
        for (int i = 0; i < 4; i++) {
            canvas.drawLine(mMarginLeft, mViewMargin + (i + 2) * ((getHeight() - mViewMargin * 2) / 5), getWidth() - mViewMargin,
                    mViewMargin + (i + 2) * ((getHeight() - mViewMargin * 2) / 5), mPaint);
        }
        //右边最后一条竖线
        canvas.drawLine(getWidth() - mViewMargin, mViewMargin  + 2f * ((getHeight() - mViewMargin * 2) / 5),
                getWidth() - mViewMargin, mViewMargin + ((getHeight() - mViewMargin * 2)), mPaint);
    }

    //绘制y轴刻度
    @SuppressLint("DefaultLocale")
    private void drawYScale(Canvas canvas) {
        canvas.drawText("总的平均油耗:" + String.format("%.2f",mallAverageOil) + "L/Km", mViewMargin + 50,
                mViewMargin + ((getHeight() - mViewMargin * 2) / 5) + 30, mPaint);
        canvas.drawText("总费用:" + String.format("%.2f",mallSpent) + "元", mViewMargin + 50,
                mViewMargin + ((getHeight() - mViewMargin * 2) / 5) + 60, mPaint);
        canvas.drawText("总加油升数:"+ String.format("%.2f",mallOilCount) + "L", mViewMargin + 50,
                mViewMargin + ((getHeight() - mViewMargin * 2) / 5) + 90, mPaint);

        canvas.drawText("近15次油耗曲线", mViewMargin + 350,
                mViewMargin + 2f * ((getHeight() - mViewMargin * 2) / 5) - 30, mPaint);
        for (int i = 0; i < mYList.size(); i++) {
            String scale = (i == 0) ? mYList.get(i) + "  L/Km" : mYList.get(i);
            canvas.drawText(scale, mViewMargin,
                    mViewMargin + (i+2) * ((getHeight() - mViewMargin * 2) / 5), mPaint);
        }
    }

    //绘制x轴刻度
    private void drawXScale(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mTextColor);
        for (int i = 0; i < mXList.size(); i++) {
            if (i == 0) {
                canvas.drawText(mXList.get(i), mMarginLeft - dpToPx(getContext(), 3),
                        getHeight() - mViewMargin + dpToPx(getContext(), 10), mPaint);
            }
            if (i != 0 && i != 15) {
                canvas.drawText(mXList.get(i), mMarginLeft + i * (getWidth() / 17),
                        getHeight() - mViewMargin + dpToPx(getContext(), 10), mPaint);
            }
            if (i == 15) {
                canvas.drawText(mXList.get(i), mMarginLeft + i * (getWidth() / 17),
                        getHeight() - mViewMargin + dpToPx(getContext(), 10), mPaint);
            }
        }
    }

    // 绘制折线图
    private void drawLineView(Canvas canvas) {
        Path path = new Path();
        if(mListPoint.isEmpty()) {
            return;
        }
        path.moveTo(mListPoint.get(0).x, mListPoint.get(0).y);
        for (int i = 1; i < this.mListPoint.size(); i++) {
            path.lineTo(mListPoint.get(i).x, mListPoint.get(i).y);
        }
        canvas.drawPath(path, mPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int old_w, int old_h) {
        super.onSizeChanged(w, h, old_w, old_h);
    }

    public void setViewData(List<String> yList, List<String> xList, List<String> allOilConsumption) {
        this.mYList = yList;
        this.mXList = xList;
        this.mallOilConsumption = allOilConsumption;
    }

    //根据手机分辨率将px 转为 dp
    private float dpToPx(Context context, float pxValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (pxValue * scale + 0.5f);
    }

    private List<Point> getPointList() {
        List<Point> mList = new ArrayList<>();
        float height = getHeight() - mViewMargin * 2;

        int i = 0;
        for(int j = mallOilConsumption.size() - 1;i < 16 && j >= 0;i++) {
            Point point = new Point();
            if (i == 0) {
                point.x = mMarginLeft;
                point.y = getHeight() - mViewMargin;
            }
            else {
                point.x = mMarginLeft + i * (getWidth() / 17);
                point.y = mViewMargin + height * (1.0f - Float.parseFloat(mallOilConsumption.get(j)) / 2.5f );
                j--;
            }
            mList.add(point);
        }

        return mList;
    }

    private static class Point {

        public float x;
        public float y;

        public Point() {

        }
    }

}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大唐不良猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值