Android 曲线覆盖图

看图

 

样例代码

layout.xml

<com.XXXXX.utils.GraphBendLine
    android:id="@+id/ghost_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_margin="40dp"
    app:node_bar_line_color="#F9FEFD"
    app:node_bar_line_dan_color="#BAEFE6"
    app:node_color="#D7F5F0"
    app:node_line_color="#8BE4D4" />

attr.xml

<declare-styleable name="nodeLine">
    <!--节点默认颜色-->
    <attr name="node_color" format="color" />
    <!--曲线颜色-->
    <attr name="node_bar_line_color" format="color" />
    <!--曲线颜色浅色-->
    <attr name="node_bar_line_dan_color" format="color" />
    <!--进度条默认颜色-->
    <attr name="node_line_color" format="color" />

</declare-styleable>

activity

GraphBendLine mLine = (GraphBendLine) findViewById(R.id.ghost_view);

timeList.add(80f);
timeList.add(0f);
timeList.add(50f);
timeList.add(20f);
timeList.add(60f);
timeList.add(20f);
timeList.add(90f);
dataList.add("3.15");
dataList.add("3.16");
dataList.add("3.17");
dataList.add("3.18");
dataList.add("3.19");
dataList.add("3.20");
dataList.add("3.21");
mLine.updateTime(timeList, dataList);

画布类

public class GraphBendLine extends View {

    private float sumHeight;//总控件的高度
    private float sumWidth;//总空间的宽度
    private float maxY;//y轴最大值
    private float minY;//y轴最小值
    private float centerY;//y轴中间值
    private String unitY;//y轴值单位
    private float maxTime;//最大的时间 用来划分单位的 最小就是20 X1.2是为了给上方和下方预留空间
    private Paint linePaint;//线的画笔
    private Paint shadowPaint;//阴影的画笔
    private Paint mPaint;//曲线画笔
    private Paint circlePaint;//圆点画笔
    private Paint circlePaint2;//圆点画笔
    private Paint scorePaint;
    private Paint textPaint;//文字的画笔
    private ArrayList<Float> dataList;//监测值
    private ArrayList<String> timeList;//底部的时间
    private float oneHeight; //每一个小段所要分成的高
    private float oneWidth;//每一个小段所要分成的宽
    private float buttomHeiht; //给底部一排日期预留出的时间
    private Path baseLinePath;//折线路径
    private float smoothness = 0.36f; //折线的弯曲率
    private Paint baseShadow;//折线下的阴影的画笔
    private String lineColor = "#CBF2ED";//节点默认颜色 "#17CAAA"
    private String barLineColor = "#BAEFE6";//曲线覆盖区颜色
    private String barLineDanColor = "#D7F5F0";//曲线覆盖区浅颜色颜色  "#F9FEFD"
    private String nodeColor = "#8BE4D4";//节点默认颜色
    private ArrayList<PointF> xyList;//储存定好的坐标点的集合

    public GraphBendLine(Context context) {
        super(context);
        initPaint();
    }

    public GraphBendLine(Context context, AttributeSet attrs) {
        super(context, attrs);

//        mContext = context;
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.nodeLine);
        lineColor = ta.getString(R.styleable.nodeLine_node_line_color);
        nodeColor = ta.getString(R.styleable.nodeLine_node_color);
        barLineColor = ta.getString(R.styleable.nodeLine_node_bar_line_color);
        barLineDanColor = ta.getString(R.styleable.nodeLine_node_bar_line_dan_color);
        ta.recycle();
        initPaint();
    }

    /**
     * 初始化画笔
     *
     * @linpaint 线条画笔
     * @shadowPaint 阴影画笔
     */
    private void initPaint() {
        //画线的画笔
        linePaint = new Paint();
        linePaint.setColor(Color.parseColor("#333333"));
        linePaint.setAntiAlias(true);
        linePaint.setTextSize(dp2px(getContext(), 9));
        linePaint.setStrokeWidth(dp2px(getContext(), 1));
        //画背景的画笔
        shadowPaint = new Paint();
        shadowPaint.setColor(Color.parseColor(lineColor));
        shadowPaint.setAntiAlias(true);
        //画最下方文字的画笔
        textPaint = new Paint();
        textPaint.setColor(Color.parseColor("#333333"));
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(dp2px(getContext(), 8));

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(Color.WHITE);
        circlePaint.setStrokeWidth(dp2px(getContext(), 2));
        circlePaint.setStyle(Paint.Style.STROKE);

        circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint2.setColor(Color.parseColor(lineColor));
        circlePaint2.setStyle(Paint.Style.FILL);

        baseShadow = new Paint();
        baseShadow.setAntiAlias(true);
        baseShadow.setColor((Color.WHITE & 0x40FFFFFF) | 0x10000000);
        baseShadow.setStyle(Paint.Style.FILL);

        buttomHeiht = dp2px(35);//线距离底部高度

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.parseColor(lineColor));
        mPaint.setStrokeWidth(dp2px(getContext(), 2));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);


        scorePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scorePaint.setStyle(Paint.Style.STROKE);
        scorePaint.setStrokeCap(Paint.Cap.ROUND);
        scorePaint.setColor(Color.parseColor("#DDDDDD"));
        scorePaint.setStrokeWidth(dp2px(0.5f));
        baseLinePath = new Path();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        sumHeight = getMeasuredHeight();
        sumWidth = getMeasuredWidth();

    }

    private void measure() {
        maxTime = maxY;//最大为300
        for (int i = 0; i < dataList.size(); i++) {
            if (maxTime <= dataList.get(i)) {
                maxTime = dataList.get(i);
            }
        }
        if (maxTime < 20) {
            maxTime = 20;
        }
        String text = "V";
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        oneHeight = ((sumHeight - buttomHeiht - 2 * rect.height()) / maxTime);
        oneWidth = sumWidth / 28;
    }

    /**
     * 设置最大最小值
     */
    public void setMaxMin(float max, float min, float center, String unit) {
        this.maxY = max;
        this.minY = min;
        this.centerY = center;
        this.unitY = unit;
    }

    /**
     * 更新阅读时间
     */

    public void updateTime(ArrayList<Float> timeList, ArrayList<String> bottomList) {
        this.dataList = timeList;
        this.timeList = bottomList;
        if (this.dataList != null && this.dataList.size() > 0 && timeList != null && timeList.size() > 0) {
            invalidate();
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (dataList == null || timeList == null) return;
        maxTime = getMaxTime(dataList);
        measure();
        toGetXy();//获取x和y的坐标

        toDrawLine(canvas);
    }

    private void toGetXy() {
        int leftWidth = dp2px(13);//距离右边宽度
        xyList = new ArrayList<>();
        for (int i = 0; i < dataList.size(); i++) {
            float x = oneWidth + i * 4 * oneWidth;
            float time = dataList.get(i);//每点时间
            float y = (sumHeight - (oneHeight * time));
            xyList.add(new PointF(x + leftWidth, y - buttomHeiht));
        }
    }

    /**
     * 画线
     */
    private void toDrawLine(Canvas canvas) {
        if (xyList == null || xyList.size() == 0) {
            return;
        }
        List<PointF> NewPoints = new ArrayList<>();
        NewPoints.addAll(xyList);
        float lX = 0;
        float lY = 0;
        baseLinePath.reset();
        baseLinePath.moveTo(NewPoints.get(0).x, NewPoints.get(0).y);
        for (int i = 1; i < NewPoints.size(); i++) {
            PointF p = NewPoints.get(i);
            PointF firstPointF = NewPoints.get(i - 1);
            float x1 = firstPointF.x + lX;
            float y1 = firstPointF.y + lY;

            PointF secondPointF = NewPoints.get(i + 1 < NewPoints.size() ? i + 1 : i);
            lX = (secondPointF.x - firstPointF.x) / 2 * smoothness;
            lY = (secondPointF.y - firstPointF.y) / 2 * smoothness;
            float x2 = p.x - lX;
            float y2 = p.y - lY;
            if (y1 == p.y) {
                y2 = y1;
            }
            baseLinePath.cubicTo(x1, y1, x2, y2, p.x, p.y);
        }
        canvas.drawPath(baseLinePath, mPaint);
        if (timeList.size() > 1) {
            for (int i = 0; i < timeList.size(); i++) {
                float x = NewPoints.get(i).x;
                float y = NewPoints.get(i).y;
                float time = dataList.get(i);
                String mThumbText = String.valueOf(time);
                Rect rect = new Rect();
                linePaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect);
                canvas.drawText(mThumbText, x - rect.width() / 2, y - dp2px(6), linePaint);//画最上面字体
                canvas.drawCircle(x, y, dp2px(3), circlePaint);
                canvas.drawCircle(x, y, dp2px(2), circlePaint2);
                Rect rectf = new Rect();
                textPaint.getTextBounds(timeList.get(i), 0, timeList.get(i).length(), rectf);
                canvas.drawText(timeList.get(i), x - rectf.width() / 2, sumHeight - dp2px(2), textPaint);//画最下方的字体
            }
            drawArea(canvas, NewPoints);
        }
        drawBottomLine(canvas);
        drawLine(canvas);

    }

    /**
     * 分割线
     *
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        //
        String text = minY + "";
        Rect rect = new Rect();
        scorePaint.getTextBounds(text, 0, text.length(), rect);
        //0
        canvas.drawText(text, 0, sumHeight - buttomHeiht - rect.height() / 2, textPaint);
        //60
        text = centerY + "";
        float y = (sumHeight - (oneHeight * centerY));
        canvas.drawText(text, 0, y - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y - buttomHeiht, sumWidth, y - buttomHeiht, scorePaint);
        //100
        text = maxY + "(" + unitY + ")";
        float y2 = (sumHeight - (oneHeight * maxY));
        canvas.drawText(text, 0, y2 - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y2 - buttomHeiht, sumWidth, y2 - buttomHeiht, scorePaint);
    }

    /**
     * 底部标线
     *
     * @param canvas
     */
    private void drawBottomLine(Canvas canvas) {
        Rect rect = new Rect();
        scorePaint.getTextBounds("0", 0, "0".length(), rect);
        canvas.drawLine(0, sumHeight - dp2px(15) - rect.height() / 2, sumWidth, sumHeight - dp2px(15) - rect.height() / 2, scorePaint);//画底部灰线
    }

    /**
     * 阴影层叠
     *
     * @param canvas
     * @param Points
     */

    private void drawArea(Canvas canvas, List<PointF> Points) {
        LinearGradient mShader = new LinearGradient(0, 0, 0, getMeasuredHeight(), new int[]{Color.parseColor(nodeColor), Color.parseColor(barLineColor), Color.parseColor(barLineDanColor)}, new float[]{0.5f, 0.65f, 0.85f}, Shader.TileMode.REPEAT);
        baseShadow.setShader(mShader);
        if (Points.size() > 0 && xyList != null && xyList.size() > 0) {
            baseLinePath.lineTo(xyList.get(Points.size() - 1).x, sumHeight - buttomHeiht);
            baseLinePath.lineTo(xyList.get(0).x, sumHeight - buttomHeiht);
            baseLinePath.close();
            canvas.drawPath(baseLinePath, baseShadow);
        }

    }

    public int dp2px(float dp) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    /**
     * 取出时间里面的最大的一个用来计算总长度
     *
     * @param timeList
     * @return
     */
    public float getMaxTime(List<Float> timeList) {
        maxTime = 0;
        for (int i = 0; i < timeList.size(); i++) {
            if (maxTime < timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime <= 10) {
            maxTime = 10;
        }
        maxTime *= 1.2;
        return maxTime;
    }

    public int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    public float dp2px(Resources resources, float dp) {
        final float scale = resources.getDisplayMetrics().density;
        return dp * scale + 0.5f;
    }

    public float sp2px(Resources resources, float sp) {
        final float scale = resources.getDisplayMetrics().scaledDensity;
        return sp * scale;
    }
}

以上就是全部实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值