美文网首页
带渐变色的折线图记录

带渐变色的折线图记录

作者: 名字不想带英文 | 来源:发表于2020-03-30 15:21 被阅读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);
        }
    }
    
    

    相关文章

      网友评论

          本文标题:带渐变色的折线图记录

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