睡眠图表

1,783 阅读5分钟

华为健康 有个图表,客户想要,,UI呢,竟然还给满足了。可辛苦了我等啊。 具体效果如下

图表

知识点如下: 1,图形绘制 圆角矩形,线,三角形 2,渐变处理 Shader

初始化操作


public class SleepChartView extends View {
    private List<SleepChartValueBean> mDataList;
    private float mWidthPixels = 0;// 屏幕的宽高
    private float mHeightPixels = 0;

    private float mTotalTime = 100f;

    private int width = 0; // view 的 整体宽度和高度
    private int height = 0;

    private int mViewWidth = 0; // 图标可用的宽度 -- 除去下方虚线 和 右侧竖线
    private int mViewHeight = 0;

    private int leftPadding = 5, rightPadding = 10, topPadding = 30, bottomPadding = 15;

    private int elementHeight = 20; // 单独的标识的高度
    private int elementLineHeight = 30;

    private Paint mMainPaint = new Paint(); // 绘制矩形的paint
    private Paint mLinePaint = new Paint();
    private Paint mVerticalLinePaint = new Paint();
    private Paint mDashPathEffectLinePaint = new Paint();

    int roundRectR = ConvertUtils.dp2px(2); //圆角角度

    private float currDrawLeftPoint = leftPadding;

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

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

    public SleepChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mWidthPixels = getResources().getDisplayMetrics().widthPixels;
        mHeightPixels = getResources().getDisplayMetrics().heightPixels;

        mMainPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mMainPaint.setStrokeWidth(5);
        mMainPaint.setAntiAlias(true);
//        mMainPaint.setColor(Color.parseColor("#A374F7"));

        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setStrokeWidth(5);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setColor(Color.parseColor("#A374F7"));

        mVerticalLinePaint.setStyle(Paint.Style.STROKE);
        mVerticalLinePaint.setStrokeWidth(ConvertUtils.dp2px(2));
        mVerticalLinePaint.setAntiAlias(true);
        mVerticalLinePaint.setColor(Color.parseColor("#CDE4E4"));


        mDashPathEffectLinePaint.setStyle(Paint.Style.STROKE);
        mDashPathEffectLinePaint.setStrokeWidth(ConvertUtils.dp2px(2));
        mDashPathEffectLinePaint.setAntiAlias(true);
        mDashPathEffectLinePaint.setColor(Color.parseColor("#806AA544"));

        mDashPathEffectLinePaint.setPathEffect(new DashPathEffect(new float[]{12, 10,}, 0));

    }

测量大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        int measuredWidth = width, measuredHeight = height;
        if (widthMode != MeasureSpec.EXACTLY) {
            measuredWidth = (int) mWidthPixels;
        }
        if (heightMode != MeasureSpec.EXACTLY) {
            measuredHeight = (int) (mHeightPixels / 3) + 40;
        }
        setMeasuredDimension(measuredWidth, measuredHeight);
    }

View的大小,初始化每个节点和连接线的高度


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;

        mViewWidth = width - leftPadding - rightPadding;
        mViewHeight = height - topPadding - bottomPadding;

        elementHeight = mViewHeight / 4;
        elementLineHeight = mViewHeight / 4 / 2;
    }


绘制


@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        canvas.drawColor(Color.WHITE);
        currDrawLeftPoint = leftPadding;
        for (int i = 0; i < mDataList.size(); i++) {
            SleepChartValueBean itemData = mDataList.get(i);
            int currTimeLong = itemData.getTimeLong();
            double timeScale = currTimeLong * 1.0 / mTotalTime;// 当前的时间占比

            float currItemWidth = (float) (mViewWidth * timeScale);
            int elementLevel = itemData.getElementLevel();

            mMainPaint.setShader(null);
            mLinePaint.setShader(null);

            int currStatus = itemData.getStatus();

// 第一个特殊处理,因为连接线处是平角
            if (0 == i) {
                Path roundRectPath = new Path();
                float left = currDrawLeftPoint;
                float top = topPadding + elementLevel * elementHeight + elementLevel * elementLineHeight;
                float right = left + currItemWidth;
                float bottom = top + elementHeight;
                RectF rectf = new RectF(left, top, right, bottom);
                roundRectPath.addRoundRect(rectf, roundRectR, roundRectR, Path.Direction.CCW);
                //   0,离床  1:清醒 2:浅水 3:深睡
                int startColor = Color.parseColor("#FD886C");
                int endColor = Color.parseColor("#A1B092");
                if (0 == currStatus) {
                    int[] colors = {Color.parseColor("#FD886C"), Color.parseColor("#C49879"), Color.parseColor("#A1A281")};
                    float[] position = {0f, 0.7f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    endColor = Color.parseColor("#A1A281");
                }
                if (1 == currStatus) {
                    int[] colors = {Color.parseColor("#4DBFE7"), Color.parseColor("#48BDC2"), Color.parseColor("#45BC9F")};
                    float[] position = {0f, 0.7f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    endColor = Color.parseColor("#A1A281");
                }
                if (2 == currStatus) {
                    int[] colors = {Color.parseColor("#3DB964"), Color.parseColor("#3FBA7A"), Color.parseColor("#41BA89")};
                    float[] position = {0f, 0.5f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    startColor = colors[2];
                }
                if (3 == currStatus) {
                    int[] colors = {Color.parseColor("#A274F6"), Color.parseColor("#7F8DC2"), Color.parseColor("#6E98AA")};
                    float[] position = {0f, 0.7f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, bottom, left, top, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    startColor = colors[2];
                }

                canvas.drawPath(roundRectPath, mMainPaint);


                int nextIndex = i + 1;
                SleepChartValueBean nextItemData = mDataList.get(nextIndex);
                if (nextItemData != null) {
                    int nextElementLevel = nextItemData.getElementLevel();
                    Path pathLine = new Path();
                    Path pathTriangle = new Path();
                    // 下一个的top
                    int nextTop = topPadding + nextElementLevel * elementHeight + nextElementLevel * elementLineHeight;
                    int nextBottom = nextTop + elementHeight;

                    if (nextElementLevel < elementLevel) {
                        // 右上
                        pathTriangle.moveTo(right - roundRectR, top);
                        pathTriangle.lineTo(right, top);
                        pathTriangle.lineTo(right, top + roundRectR);
                        pathLine.moveTo(right, top + roundRectR);
                        pathLine.lineTo(right, nextBottom - roundRectR);

                        if (nextItemData.getStatus() == 0) {
                            int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#9EA888")};
                            float[] position = {0f, 0.5f, 1.0f};
                            LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
                            mLinePaint.setShader(linearGradient);
                        }

                        if (nextItemData.getStatus() == 1) {
                            int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#45BC9F")};
                            float[] position = {0f, 0.5f, 1.0f};
                            LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
                            mLinePaint.setShader(linearGradient);
                        }

                        if (nextItemData.getStatus() == 2) {
                            int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#40BA7F")};
                            float[] position = {0f, 0.5f, 1.0f};
                            LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
                            mLinePaint.setShader(linearGradient);
                        }


                    }

                    if (nextElementLevel > elementLevel) {
                        //右下
                        pathTriangle.moveTo(right - roundRectR, bottom);
                        pathTriangle.lineTo(right, bottom - roundRectR);
                        pathTriangle.lineTo(right, bottom);
                        pathLine.moveTo(right, bottom - roundRectR);
                        pathLine.lineTo(right, nextTop + roundRectR);

                        if (nextItemData.getStatus() == 2) {
                            int[] colors = {endColor, Color.parseColor("#5ABF8C"), Color.parseColor("#3FB975")};
                            float[] position = {0f, 0.6f, 1.0f};
                            LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
                            mLinePaint.setShader(linearGradient);

                        }


                        if (nextItemData.getStatus() == 3) {
                            int[] colors = {endColor, Color.parseColor("#5AAA8A"), Color.parseColor("#7990BA")};
                            float[] position = {0f, 0.6f, 1.0f};
                            LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
                            mLinePaint.setShader(linearGradient);
                        }


                    }
                    pathTriangle.close();

                    canvas.drawPath(pathLine, mLinePaint);


                    if (currItemWidth > roundRectR * 2) {
                        canvas.drawPath(pathTriangle, mMainPaint);
                    }
                }
            } else {
                Path path = new Path();
                float left = currDrawLeftPoint;
                float top = topPadding + elementLevel * elementHeight + elementLevel * elementLineHeight;
                float right = left + currItemWidth;
                float bottom = top + elementHeight;

                RectF rectf = new RectF(left, top, right, bottom);
                path.addRoundRect(rectf, roundRectR, roundRectR, Path.Direction.CCW);

                int startColor = Color.parseColor("#6E98AA");
                int endColor = Color.parseColor("#A1B092");
                //   0,离床  1:清醒 2:浅水 3:深睡
                if (0 == currStatus) {
                    int[] colors = {Color.parseColor("#FD886C"), Color.parseColor("#C49879"), Color.parseColor("#A1A281")};
                    float[] position = {0f, 0.7f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    endColor = Color.parseColor("#A1A281");
                }
                if (1 == currStatus) {
                    int[] colors = {Color.parseColor("#4DBFE7"), Color.parseColor("#48BDC2"), Color.parseColor("#45BC9F")};
                    float[] position = {0f, 0.7f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    endColor = Color.parseColor("#45BC9F");
                }
                if (2 == currStatus) {
                    int[] colors = {Color.parseColor("#3DB964"), Color.parseColor("#3FBA7A"), Color.parseColor("#41BA89")};
                    float[] position = {0f, 0.5f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, top, left, bottom, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    startColor = colors[2];
                }
                if (3 == currStatus) {
                    int[] colors = {Color.parseColor("#A274F6"), Color.parseColor("#7F8DC2"), Color.parseColor("#6E98AA")};
                    float[] position = {0f, 0.7f, 1.0f};
                    LinearGradient linearGradient = new LinearGradient(left, bottom, left, top, colors, position, Shader.TileMode.CLAMP);
                    mMainPaint.setShader(linearGradient);
                    startColor = colors[2];
                }


                canvas.drawPath(path, mMainPaint);

                SleepChartValueBean afterItemData = mDataList.get(i - 1);
                int afterLevel = afterItemData.getElementLevel();
                Path pathTriangle = new Path();

                if (elementLevel > afterLevel) {
                    pathTriangle.moveTo(left + roundRectR, top);//定位到 三角形右角
                    pathTriangle.lineTo(left + 1, top - roundRectR);
                    pathTriangle.lineTo(left + 1, top + roundRectR);
                }

                if (elementLevel < afterLevel) {
                    // 左下角 三角形
                    pathTriangle.moveTo(left + roundRectR, bottom);//定位到 三角形左角
                    pathTriangle.lineTo(left + 1, bottom - roundRectR);
                    pathTriangle.lineTo(left + 1, bottom + roundRectR);
                }
                pathTriangle.close();
                if (currItemWidth > roundRectR * 2) {
                    canvas.drawPath(pathTriangle, mMainPaint);
                }

                if (i < mDataList.size() - 1) {
                    SleepChartValueBean nextItemData = mDataList.get(i + 1);
                    // 是否有下一个,如果有,则绘制抵达的直线
                    if (nextItemData != null) {
                        int nextElementLevel = nextItemData.getElementLevel();
                        Path pathLine = new Path();
                        Path nextPathTriangle = new Path();
                        // 下一个的top
                        int nextTop = topPadding + nextElementLevel * elementHeight + nextElementLevel * elementLineHeight;
                        int nextBottom = nextTop + elementHeight;

                        if (nextElementLevel < elementLevel) {
                            // 右上
                            nextPathTriangle.moveTo(right - roundRectR, top);
                            nextPathTriangle.lineTo(right - 1, top - roundRectR);
                            nextPathTriangle.lineTo(right - 1, top + roundRectR);
                            pathLine.moveTo(right, top + roundRectR);
                            pathLine.lineTo(right, nextBottom - roundRectR);

                            if (nextItemData.getStatus() == 0) {
                                int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#9EA888")};
                                float[] position = {0f, 0.5f, 1.0f};
                                LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
                                mLinePaint.setShader(linearGradient);
                            }

                            if (nextItemData.getStatus() == 1) {
                                int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#45BC9F")};
                                float[] position = {0f, 0.5f, 1.0f};
                                LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
                                mLinePaint.setShader(linearGradient);
                            }

                            if (nextItemData.getStatus() == 2) {
                                int[] colors = {startColor, Color.parseColor("#3FBA79"), Color.parseColor("#40BA7F")};
                                float[] position = {0f, 0.5f, 1.0f};
                                LinearGradient linearGradient = new LinearGradient(right, top, right, nextBottom, colors, position, Shader.TileMode.CLAMP);
                                mLinePaint.setShader(linearGradient);
                            }


                        }

                        if (nextElementLevel > elementLevel) {
                            //右下
                            nextPathTriangle.moveTo(right - roundRectR, bottom);
                            nextPathTriangle.lineTo(right - 1, bottom - roundRectR);
                            nextPathTriangle.lineTo(right - 1, bottom + roundRectR);
                            pathLine.moveTo(right, bottom - roundRectR);
                            pathLine.lineTo(right, nextTop + roundRectR);


                            if (nextItemData.getStatus() == 2) {
                                int[] colors = {endColor, Color.parseColor("#5ABF8C"), Color.parseColor("#3FB975")};
                                float[] position = {0f, 0.6f, 1.0f};
                                LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
                                mLinePaint.setShader(linearGradient);

                            }


                            if (nextItemData.getStatus() == 3) {
                                int[] colors = {endColor, Color.parseColor("#5AAA8A"), Color.parseColor("#7990BA")};
                                float[] position = {0f, 0.6f, 1.0f};
                                LinearGradient linearGradient = new LinearGradient(right, bottom - roundRectR, right, nextTop, colors, position, Shader.TileMode.CLAMP);
                                mLinePaint.setShader(linearGradient);
                            }
                        }
                        nextPathTriangle.close();
                        canvas.drawPath(pathLine, mLinePaint);
                        if (currItemWidth > roundRectR * 2) {
                            canvas.drawPath(nextPathTriangle, mMainPaint);
                        }
                    }
                }


            }
            currDrawLeftPoint = currDrawLeftPoint + currItemWidth;
        }

        canvas.drawLine(currDrawLeftPoint + ConvertUtils.dp2px(2), 0, currDrawLeftPoint + ConvertUtils.dp2px(2), getHeight() - ConvertUtils.dp2px(1), mVerticalLinePaint);

        Path bottomLinePath = new Path();
        bottomLinePath.moveTo(getWidth(), getHeight() - ConvertUtils.dp2px(1));
        bottomLinePath.lineTo(0, getHeight() - ConvertUtils.dp2px(1));

        canvas.drawPath(bottomLinePath, mDashPathEffectLinePaint);

        canvas.restore();
    }

    public void setData(List<SleepChartValueBean> list) {
        if (list == null || list.isEmpty()) {
            mDataList = new ArrayList<>();
        } else {
            mDataList = list;
        }
        postInvalidate();
    }

    public void setTotalTime(float totalTime) {
        mTotalTime = totalTime;
    }
}

下载Chart

参考文章: www.gcssloop.com/customview/…

blog.csdn.net/harvic88092…