实现简单的自定义曲线图

news2024/11/20 7:16:04

实现的效果

 

废话不多说看源码

package com.unite.module_index_pad.utils;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;


/**
 * 描述:自定义曲线
 * Version:1.0
 * Author: yangy
 * Created by:2023-06-27
 */

public class GraphBendLine extends View {

    private float sumHeight;//总控件的高度
    private float sumWidth;//总空间的宽度
    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<Integer> timeList;//读书时间
    private ArrayList<String> dataList;//底部的时间
    private float oneHeight; //每一个小段所要分成的高
    private float oneWidth;//每一个小段所要分成的宽
    private float buttomHeiht; //给底部一排日期预留出的时间
    private Path baseLinePath;//折线路径
    private float smoothness = 0.36f; //折线的弯曲率
    private Paint baseShadow;//折线下的阴影的画笔
    private ArrayList<PointF> xyList;//储存定好的坐标点的集合

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

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

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

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(Color.parseColor("#8BE4D4"));
        circlePaint.setStrokeWidth(dp2px(getContext(), 2));
        circlePaint.setStyle(Paint.Style.STROKE);

        circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint2.setColor(Color.WHITE);
        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("#17CAAA"));
        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 = 100;//最大分数为100
        for (int i = 0; i < 7; i++) {
            if (maxTime <= timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime < 20) {
            maxTime = 20;
        }
        String text = "分";
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        oneHeight = ((sumHeight - buttomHeiht - 2 * rect.height()) / maxTime);
        oneWidth = sumWidth / 28;
    }

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

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


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

        toDrawLine(canvas);
    }

    private void toGetXy() {
        int leftWidth = dp2px(13);//距离右边宽度
        xyList = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            float x = oneWidth + i * 4 * oneWidth;
            float time = timeList.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);
        for (int i = 0; i < NewPoints.size(); i++) {
            float x = NewPoints.get(i).x;
            float y = NewPoints.get(i).y;
            int time = timeList.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(dataList.get(i), 0, dataList.get(i).length(), rectf);
            canvas.drawText(dataList.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 = "0";
        Rect rect = new Rect();
        scorePaint.getTextBounds(text, 0, text.length(), rect);
        //0
        canvas.drawText(text, 0, sumHeight - buttomHeiht - rect.height() / 2, textPaint);
        //60
        text = "60";
        float y = (sumHeight - (oneHeight * 60));
        canvas.drawText(text, 0, y - buttomHeiht - rect.height() / 2 - dp2px(4), textPaint);
        canvas.drawLine(0, y - buttomHeiht, sumWidth, y - buttomHeiht, scorePaint);
        //100
        text = "100(分)";
        float y2 = (sumHeight - (oneHeight * 100));
        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("#BAEFE6"), Color.parseColor("#D7F5F0"), Color.parseColor("#F9FEFD")}, 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<Integer> timeList) {
        maxTime = 0;
        for (int i = 0; i < 7; 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;
    }
}

在xml里面根据包名地址应该自定义View类

<com.XX.XX.utils.GraphBendLine
    android:id="@+id/ghost_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_margin="40dp" />

最后在Activity里面获取View,传对应的数据到自定义VIew就可以实现简单的曲线图了

private ArrayList<Integer> timeList = new ArrayList<>();//读书时间
private ArrayList<String> dataList = new ArrayList<>();//底部的时间
GraphBendLine mLine = (GraphBendLine)findViewById(R.id.ghost_view);
timeList.add(80);
timeList.add(0);
timeList.add(50);
timeList.add(20);
timeList.add(60);
timeList.add(20);
timeList.add(90);
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);

引用自:Android 实现一个自定义曲线图_android 曲线图_隔壁小王66的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/691389.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈“信创”时代,恒辉软件发展前景

随着中国的发展&#xff0c;很多技术因为一些原因越来越受制于人&#xff0c;尤其是上游核心技术。为了解决这个问题&#xff0c;中国明确了“数字中国”建设战略&#xff0c;抢占数字经济产业链制高点。 自主创新与国产化已成为我国实现科技强国、经济强国的发展趋势与行业共…

MySQL之MHA高可用配置及故障切换实例

目录 一、MHA概述1.1 什么是MHA&#xff1f;1.2 MHA的组成<font colorblue>MHA Node &#xff08;数据节点&#xff09;<font colorblue>MHA Manager &#xff08;管理节点&#xff09;1.3 MHA 的特点 二、 MHA搭建准备2.1 实验思路2.2 实验准备 三、 MHA搭建的步骤…

rust安装

文章目录 1.官网下载2.安装3.安装验证4.打开本地文档5.安装插件6.HelloWorld①新建项目目录使用VSCode打开②新建rs文件③编译④运行 7.HelloCargo①新建项目目录使用VSCode打开②cargo build③cargo run④cargo check⑤为发布构建 8.更新与卸载 1.官网下载 官网地址&#xff…

企业数字化转型的意义及影响,你了解多少?

一、什么叫数字化转型 数字化转型是指利用数字技术和互联网的力量&#xff0c;对传统的商业模式、组织结构和流程进行全面的改造和升级&#xff0c;以适应快速变化的数字时代。它是企业和组织在面对数字化冲击和竞争压力时&#xff0c;主动采取的一种战略性调整。 数字化转型…

超级实用的思维导图软件

如果你正在寻找一款超级实用的思维导图软件&#xff0c;那么我强烈推荐你使用ProcessOn。这款软件不仅功能强大&#xff0c;而且易于使用&#xff0c;可以帮助你更好地组织和管理工作流程、学习笔记、项目管理等。 首先&#xff0c;让我们来看看ProcessOn的优点。它提供了丰富的…

@Valid @NotEmpty 数据为空返回message回前端

当添加注解NotEmpty的字段为空时&#xff0c;返回一大段异常数据。 如果想要把NotEmpty中的message返回给前端&#xff0c; 添加代码&#xff1a; ControllerAdvice public class GlobalExceptionHandler {ResponseBodyExceptionHandler(value MethodArgumentNotValidExcep…

中文词频统计及绘制词云图2

import jieba import pandas as pd import numpy as np import PIL.Image as Image from wordcloud import WordCloud#读取stopwords.txt with open(stopwords.txt,r,encoding utf-8) as f:st f.readlines()stopwords [i.strip() for i in st]with open(中国共产主义青年团第…

PMP®证书增持 CSPM-2证书,3分钟提交申请

2023年6月起&#xff0c;持有PMP证书的朋友可以直接增持一个同等级证书CSPM-2&#xff0c;不用重新考试&#xff0c;不用重新学习&#xff0c;原PMP证书不影响正常使用&#xff0c;相当于多了一个国标项目管理领域的证书。 第一步准备资料 1、填写能力评价表 2、提供2张2寸蓝底…

spark应用----统计分析电商网站的用户行为数据

目录 项目说明 题目一&#xff1a;Top5热门品类 题目二&#xff1a;Top5热门品类中每个品类的Top5活跃Session统计 scala实现 新建maven项目结构如下 配置pom.xml文件 scala代码 python实现 项目说明 本项目的数据是采集电商网站的用户行为数据&#xff0c;主要包含用户的4…

自制GPD Win2底壳

直接看效果吧&#xff0c;壳子做了一个月&#xff0c;算是从0开始吧&#xff0c; 打样就打了好几套&#xff0c;最后还差点小细节没做好&#xff0c;整体效果还算满意。

资深老鸟整理,性能测试平均负载详情,一篇足够...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 uptime 命令 每次…

6.用python写网络爬虫,表单交互

在前面几章中&#xff0c;我们下载的静态网页总是返回相同的内容。而在本章中&#xff0c;我们将与网页进行交互 根据用户输入返回对应的内容。本章将包含如下几个主题&#xff1a; 发送 POST 请求提交表单&#xff1a; 使用 cookie 登录网站&#xff1a; 用于简化表单提交的高…

EasyExcel概述

首先导入依赖 <dependencies><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.poi</groupId><…

element 表格复选框设置禁用

禁用之后的效果&#xff0c;移入显示不可选中 <el-table :data"tableData" :row-class-name"tableRowClassName" border height"500" style"width: 100%" selection-change"handleSelectionChange"><el-table-colu…

项目风险管理6大黄金法则

在软件项目管理过程中&#xff0c;风险无处不在。风险的不确定性&#xff0c;往往导致项目延期、费用增加对项目保质保量交付造成极大影响。 如何更好地进行风险管理&#xff0c;以积极的态度处理项目风险&#xff0c;最大程度减轻风险对项目的威胁&#xff0c;就显得尤为重要。…

2023年,推荐这5款主流低代码开发平台

近几年&#xff0c;在技术领域低代码是比较热门的话题&#xff0c;低代码是基于可视化和模型驱动理念&#xff0c;结合云原生与多端体验技术&#xff0c;它能够在多数业务场景下实现大幅度的提效降本&#xff0c;为专业开发者提供了一种全新的高生产力开发范式。 低代码平台对…

uCOSiii的默认任务

uCOS有uCOSii和uCOSiii,这两个都是一个可裁剪、可剥夺型的多任务内核。 uCOSiii没有任务数限制&#xff0c;uCOSiii内部任务有5个&#xff1a; 中断服务服务管理任务&#xff0c;时钟节拍任务&#xff0c;定时器任务 &#xff0c;统计任务&#xff0c;空闲任务。 1、优先级…

C语言程序环境和预处理(1)

本章主要以图片和文字的形式给大家讲解 程序的翻译环境和程序的执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境&#xff0c;它用于实际执行代码 2. 详解编译…

ModbusRTU协议封装,控制RJ45报警器,复制一下就能用哦~

本文只对 写保持寄存器 HoldingRegister 做操作,其他类型的寄存器方法方法也在ModbusWriteOrRead类中,可自行测试。 报警器设备型号(USB版):JD01AX07 01 设备外观及亮灯: 文档说明-部分: 注: 以下图第一个绿灯开启的二进制命令为例: 01 06:寄存器类型 00 00:…

机器学习6:使用 TensorFlow 的训练线性回归模型

纸上得来终觉浅&#xff0c;绝知此事要躬行。前面 5 篇文章介绍了机器学习相关的部分基础知识&#xff0c;在本章&#xff0c;笔者将讲解基于 TensorFlow 实现一个简单的线性回归模型&#xff0c;以便增强读者对机器学习的体感。 目录 1.环境准备 1.1 安装 Python3 1.2 安装…