带渐变色的折线图记录

2020-03-30  本文已影响0人  名字不想带英文

单纯因为涉及的基础知识有点多,所以记录一下,成品图如下


image.png
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import com.eluton.helper.ScreenHelper;
import com.eluton.mylibrary.R;
import com.eluton.utils.LogUtil;

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

public class BrokenView extends View {
    private Context context;
    private Paint paintLine;//折线画笔
    private Paint paintShare;//折线图渐变颜色背景
    private Paint paintText;//文字画笔
    private Paint paintCircle;//园画笔

    private Path pathLine;//折线
    private Path pathShare;//折线渐变路径(一个闭合的图)

    private Shader shader;
    private int lineColor;
    private int[] shareColor = new int[]{getResources().getColor(R.color.green_00b395_40), getResources().getColor(R.color.green_00b395_30), getResources().getColor(R.color.tran)};
//            Color.argb(130, 255, 86, 86), Color.argb(36, 255, 86, 86),
//            Color.argb(0, 255, 86, 86)};

    private int max = 180;//初始最大值为180(y轴),数组中最大值大于180已最大那个为准,下面有setMax方法
    private double unit_h;//每个值的高度
    private int[] pad = new int[4];//左、上、右、下4个内间距

    private int width_broken;//折线图宽度(总宽度-左右内间距)
    private int height_broken;
    private int width;
    private int height;
    private int dx;//一周(横行)有6格,这个表示每格的宽度
    private int dy;//竖行有3格,这个表示每格的高度
    private Point pointStart = new Point();//折线图开始点

    private int text_height;//文字高度
    private int text_vertical_width;//垂直文字宽度(max的宽度)
    private int text_horizontal_width;//水平文字宽度(max的宽度)
    private int text_horizontal_x;//文字水平方向开始的x位置
    private int text_horizontal_y;//文字水平方向开始的y位置
    private int text_vertical_x;//文字垂直方向开始的x位置
    private int text_vertical_y;//文字垂直方向开始的y位置

    private int radius = 3;//内圆半径(最小直径12sp,20 )

    private int size1;
    private int size6;
    private int srcSize;

    private List<Integer> list_int = new ArrayList<>();

    public BrokenView(Context context) {
        this(context, null);
    }

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

    public BrokenView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        init();
    }

    private Bitmap bitmap;
    private Rect mSrcRect = new Rect();//图片原大小
    private Rect mDestRect = new Rect();//图片在view要展示的大小
//    private ScreenHelper screenHelper;

    private void init() {
//        screenHelper = new ScreenHelper(context);//
        size1 = ScreenHelper.dip2px(context, 1);//将dp转为px
        size6 = 6 * size1;
        srcSize = 11 * size1;
        radius = 3 * size1 / 2;
//        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.timg);
//        mSrcRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        imgs[0] = BitmapFactory.decodeResource(getResources(), R.mipmap.studyrecord_bad);//表情图片,下同,随便找找吧
        imgs[1] = BitmapFactory.decodeResource(getResources(), R.mipmap.studyrecord_soso);
        imgs[2] = BitmapFactory.decodeResource(getResources(), R.mipmap.studyrecord_good);
        imgs[3] = BitmapFactory.decodeResource(getResources(), R.mipmap.studyrecord_awsome);


        pad[0] = ScreenHelper.dip2px(context, 40);
        pad[1] = ScreenHelper.dip2px(context, 40);
        pad[2] = ScreenHelper.dip2px(context, 40);
        pad[3] = ScreenHelper.dip2px(context, 40);


//        lineColor = getResources().getColor(R.color.colorPrimary);

//  初始化画线的画笔,包括背景线和折线
        paintLine = new Paint();
        paintLine.setStyle(Paint.Style.STROKE);
        paintLine.setAntiAlias(true);
        paintLine.setStrokeWidth(2);
//        paintLine.setColor(lineColor);

//初始化圆的画笔
        paintCircle = new Paint();
        paintCircle.setStyle(Paint.Style.FILL);
        paintCircle.setAntiAlias(true); //抗锯齿
        paintCircle.setColor(Color.WHITE);
        paintCircle.setTextAlign(Paint.Align.LEFT);

        //  初始化文字画笔
        paintText = new Paint();
        paintText.setStyle(Paint.Style.FILL);
        paintText.setAntiAlias(true); //抗锯齿
        paintText.setTextSize(ScreenHelper.dip2px(context, 12));
        paintText.setColor(getResources().getColor(R.color.green_00b395));
        paintText.setTextAlign(Paint.Align.LEFT);
        text_height = (int) measureTextHeight(paintText);
//        获取文字(120和02.20)的宽度
        text_vertical_width = (int) paintText.measureText("120");
        text_horizontal_width = (int) paintText.measureText("02.20");


        paintShare = new Paint();

        pathLine = new Path();
        pathShare = new Path();
    }

    //获取文字高度
    public static float measureTextHeight(Paint paint) {
        float height = 0f;
        if (null == paint) {
            return height;
        }
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        height = fontMetrics.descent - fontMetrics.ascent;
        return height;
    }

    private String date[] = {"02.16", "02.17", "02.18", "02.19", "02.20", "02.21", "今日"};//底部日期
    private Bitmap imgs[] = new Bitmap[4];//数值顶部图片,没有图片的可以注释掉

    public void updateDate(List<String> list_date) {//更新底部日期
        for (int i = 0; i < date.length; i++) {
            date[i] = list_date.get(i);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initWH();
        unit_h = height_broken / (double) max;//每个值占得高度
//        LogUtil.i(max + "unit_h:" + unit_h);
        if (width_broken > 0 && height_broken > 0 && unit_h != 0) {//折线图宽高大于0才能开始画
            paintLine.setColor(getResources().getColor(R.color.green_00b395));
            paintLine.setStrokeWidth(0.2f);

            text_vertical_x = pointStart.x - text_vertical_width - size6;
            text_vertical_y = pointStart.y + text_height / 2 - 2;

            text_horizontal_x = pointStart.x - text_horizontal_width / 2;
            text_horizontal_y = pointStart.y + text_height * 2 + 2;

            mPointList.clear();
            for (int i = 0; i < list_int.size(); i++) {//画顶部图片和底部文字
                int x = pointStart.x + dx * i;
                int y = (int) (pointStart.y - list_int.get(i) * unit_h);
                mPointList.add(new Point(x, y));
            }

            for (int i = 0; i < 7; i++) {//画竖线背景
                if (list_int.size() == i + 1) {
                    paintText.setColor(getResources().getColor(R.color.black_333333));
                } else {
                    paintText.setColor(getResources().getColor(R.color.black_999999));
                }
                int x = pointStart.x + dx * i;
//                int y = (int) (pointStart.y - list_int.get(i) * unit_h);
//                if (i != 0 && i != 6) {
                canvas.drawLine(x, pointStart.y, x, pointStart.y - height_broken - srcSize, paintLine);
//                }
                canvas.drawText(date[i], text_horizontal_x + i * dx, text_horizontal_y, paintText);
            }

            paintText.setColor(getResources().getColor(R.color.green_00b395_30));
            for (int i = 1; i < 4; i++) {//画横向背景
                canvas.drawLine(pointStart.x - size6, pointStart.y - i * dy, width_broken + pointStart.x + srcSize, pointStart.y - i * dy, paintLine);
                canvas.drawText(String.valueOf(max * i / 3), text_vertical_x, text_vertical_y - i * dy, paintText);
            }

//            paintText.setColor(getResources().getColor(R.color.green_00b395));

            if (mPointList.size() > 1) {//画折线图
                measurePath(canvas);
            }

            for (int i = 0; i < list_int.size(); i++) {//画各个点
                int x = pointStart.x + dx * i;
                int y = (int) (pointStart.y - list_int.get(i) * unit_h);

                paintCircle.setColor(getResources().getColor(R.color.green_00b395));
                canvas.drawCircle(x, y, 3 * size1, paintCircle);
                paintCircle.setColor(Color.WHITE);
                canvas.drawCircle(x, y, 2 * size1 + 1, paintCircle);
                paintCircle.setColor(getResources().getColor(R.color.green_00b395));
                canvas.drawCircle(x, y, radius, paintCircle);
            }

            if (isLogin) {
                for (int i = 0; i < list_int.size(); i++) {//画各个点的顶部图片和底部文字
                    int x = pointStart.x + dx * i;
                    int y = (int) (pointStart.y - list_int.get(i) * unit_h);

                    int proNum = list_int.get(i);
                    if (proNum < 30) {
                        bitmap = imgs[0];
                    } else if (proNum < 60) {
                        bitmap = imgs[1];
                    } else if (proNum < 120) {
                        bitmap = imgs[2];
                    } else {
                        bitmap = imgs[3];
                    }
                    mSrcRect.left = 0;
                    mSrcRect.top = 0;
                    mSrcRect.right = bitmap.getWidth();
                    mSrcRect.bottom = bitmap.getHeight();

                    mDestRect.left = x - srcSize / 2;
                    mDestRect.top = y - srcSize - size6;
                    mDestRect.right = x + srcSize / 2;
                    mDestRect.bottom = y - size6;
//                Rect mDestRect = new Rect(x - srcSize / 2, y - srcSize - size6 * 2, x + srcSize / 2, y - size6 * 2);
                    canvas.drawBitmap(bitmap, mSrcRect, mDestRect, null);
//                第一个Rect 代表要绘制的bitmap 区域,第二个 Rect 代表的是要将bitmap

                    int num_width = (int) paintText.measureText(String.valueOf(list_int.get(i)));//每个值得宽度
                    if (i + 1 == list_int.size()) {
                        int textLeft = (int) (x - num_width / (float) 2);
                        RectF rectTextBg = new RectF();
                        rectTextBg.left = textLeft - 3 * size1;
                        rectTextBg.bottom = y + size1 * 4 + text_height;
                        rectTextBg.right = rectTextBg.left + size6 + num_width + 1;
                        rectTextBg.top = y + size1 * 4;
                        int r = text_height / 2 - 1;
                        paintText.setColor(getResources().getColor(R.color.green_00b395));
                        canvas.drawRoundRect(rectTextBg, r, r, paintText);
                        paintText.setColor(getResources().getColor(R.color.white));
                        canvas.drawText(String.valueOf(list_int.get(i)), x - num_width / (float) 2, y + size6 / 3 + text_height, paintText);
//                    paintText.setColor(getResources().getColor(R.color.red_ff695e));
                    } else {
                        paintText.setColor(getResources().getColor(R.color.green_00b395));
                        canvas.drawText(String.valueOf(list_int.get(i)), x - num_width / (float) 2, y + size6 / 3 + text_height, paintText);
                    }
                }
            }
        }
    }

    public void test() {//测试用得
        isLogin = true;
        ArrayList<Integer> list = new ArrayList<>();
        list.add(0);
        list.add(98);
        list.add(48);
        list.add(180);
        list.add(60);
        list.add(108);
        list.add(150);
        updateList(list);
    }

    private boolean isLogin = false;//是否登录

    public void setLogin(boolean flag) {
        isLogin = flag;
        if (isLogin) {
            test();
        } else {
            ArrayList<Integer> list = new ArrayList<>();
            for (int i = 0; i < 7; i++) {
                list.add(0);
            }
            updateList(list);
        }
    }

    public void updateList(List<Integer> list_i) {
//        initWH();
        list_int.clear();
        max = 0;
        for (int i = 0; i < list_i.size(); i++) {
            int num = list_i.get(i);
            if (num > max) {
                max = num;
            }
            list_int.add(num);
        }
        if (max == 0 || max < 180) {
            max = 180;
        }
        if (width > 0) {
            postInvalidate();
        }
    }

    //    获取view的宽高,同时定义折线图左下角的点
    private void initWH() {
        if (width == 0) {
            width = getWidth();
            height = getHeight();

            width_broken = width - pad[0] - pad[2];
            height_broken = height - pad[1] - pad[3];

            shader = new LinearGradient(0, 0, 0, height, shareColor, null, Shader.TileMode.CLAMP);
            paintShare.setShader(shader);
            dx = width_broken / 6;
            dy = height_broken / 3;
            LogUtil.i("height_broken:" + height_broken);
            pointStart.x = pad[0];
            pointStart.y = height - pad[3];
//            test();
        }
    }

    private List<Point> mPointList = new ArrayList<>();
    private float lineSmoothness = 0.16f;//可自己修改值看变化

    //    用曲线连接各点
    private void measurePath(Canvas canvas) {
        paintLine.setStrokeWidth(size1);//改成2像素
        //保存曲线路径
        Path mPath = new Path();
        //保存辅助线路径
        Path mAssistPath = new Path();
        float prePreviousPointX = Float.NaN;
        float prePreviousPointY = Float.NaN;
        float previousPointX = Float.NaN;
        float previousPointY = Float.NaN;
        float currentPointX = Float.NaN;
        float currentPointY = Float.NaN;
        float nextPointX;
        float nextPointY;

        final int lineSize = mPointList.size();
        for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {
            if (Float.isNaN(currentPointX)) {
                Point point = mPointList.get(valueIndex);
                currentPointX = point.x;
                currentPointY = point.y;
            }
            if (Float.isNaN(previousPointX)) {
                //是否是第一个点
                if (valueIndex > 0) {
                    Point point = mPointList.get(valueIndex - 1);
                    previousPointX = point.x;
                    previousPointY = point.y;
                } else {
                    //是的话就用当前点表示上一个点
                    previousPointX = currentPointX;
                    previousPointY = currentPointY;
                }
            }

            if (Float.isNaN(prePreviousPointX)) {
                //是否是前两个点
                if (valueIndex > 1) {
                    Point point = mPointList.get(valueIndex - 2);
                    prePreviousPointX = point.x;
                    prePreviousPointY = point.y;
                } else {
                    //是的话就用当前点表示上上个点
                    prePreviousPointX = previousPointX;
                    prePreviousPointY = previousPointY;
                }
            }

            // 判断是不是最后一个点了
            if (valueIndex < lineSize - 1) {
                Point point = mPointList.get(valueIndex + 1);
                nextPointX = point.x;
                nextPointY = point.y;
            } else {
                //是的话就用当前点表示下一个点
                nextPointX = currentPointX;
                nextPointY = currentPointY;
            }

            if (valueIndex == 0) {
                // 将Path移动到开始点
                mPath.moveTo(currentPointX, currentPointY);
                mAssistPath.moveTo(currentPointX, currentPointY);
            } else {
                // 求出控制点坐标
                final float firstDiffX = (currentPointX - prePreviousPointX);
                final float firstDiffY = (currentPointY - prePreviousPointY);
                final float secondDiffX = (nextPointX - previousPointX);
                final float secondDiffY = (nextPointY - previousPointY);
                final float firstControlPointX = previousPointX + (lineSmoothness * firstDiffX);
                final float firstControlPointY = previousPointY + (lineSmoothness * firstDiffY);
                final float secondControlPointX = currentPointX - (lineSmoothness * secondDiffX);
                final float secondControlPointY = currentPointY - (lineSmoothness * secondDiffY);
                //画出曲线
                mPath.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,
                        currentPointX, currentPointY);
                //将控制点保存到辅助路径上
                mAssistPath.lineTo(firstControlPointX, firstControlPointY);
                mAssistPath.lineTo(secondControlPointX, secondControlPointY);
                mAssistPath.lineTo(currentPointX, currentPointY);
            }

            // 更新值,
            prePreviousPointX = previousPointX;
            prePreviousPointY = previousPointY;
            previousPointX = currentPointX;
            previousPointY = currentPointY;
            currentPointX = nextPointX;
            currentPointY = nextPointY;
        }
        PathMeasure mPathMeasure = new PathMeasure(mPath, false);
        canvas.drawPath(mPath, paintLine);

//        pathShare.lineTo(pointStart.x, mPointList.get(0).y);
        pathShare.reset();
        pathShare.addPath(mPath);
        pathShare.lineTo(pointStart.x + dx * (list_int.size() - 1), pointStart.y);
        pathShare.lineTo(pointStart.x, pointStart.y);
        pathShare.close();
        canvas.drawPath(pathShare, paintShare);
    }
}

上一篇下一篇

猜你喜欢

热点阅读