接着上篇,今天介绍一下曲线图 / 折线图的实现方法,先上效果图:

image.png

曲线图很简单了,坐标轴跟刻度线跟上篇柱状图的绘制一样一样滴,绘制曲线图,关键就是确定好Y轴的每个点,然后用绘制曲线的方法,把点连起来就OK了。

(1)确定数据在坐标轴上对应的每个点

 /*** 根据传入的数据,确定绘制的点** @return*/private Point[] initPoint() {Point[] points = new Point[mDatas.size()];for (int i = 0; i < mDatas.size(); i++) {Integer ybean = mDatas.get(i);int drawHeight = (int) (startY * 1.0 - (ybean * yAxisSpace * 1.0 / yIncreaseValue));int startx = startX + xAxisSpace * i;points[i] = new Point(startx, drawHeight);}Log.e("TAG", "startX=" + startX + "---startY=" + startY);return points;}

(2)将点连接起来,这里使用cubicTo绘制贝塞尔曲线。

image.png

/*** 绘制曲线-曲线图** @param canvas*/private void drawScrollLine(Canvas canvas) {Point startp;Point endp;for (int i = 0; i < mPoints.length - 1; i++) {startp = mPoints[i];endp = mPoints[i + 1];int wt = (startp.x + endp.x) / 2;Point p3 = new Point();Point p4 = new Point();p3.y = startp.y;p3.x = wt;p4.y = endp.y;p4.x = wt;Path path = new Path();path.moveTo(startp.x, startp.y);path.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);canvas.drawPath(path, mPaint);}}/*** 绘制直线-折线图** @param canvas*/private void drawLine(Canvas canvas) {Point startp;Point endp;for (int i = 0; i < mPoints.length - 1; i++) {startp = mPoints[i];endp = mPoints[i + 1];canvas.drawLine(startp.x, startp.y, endp.x, endp.y, mPaint);}}

完整代码:

package com.example.jojo.learn.customview;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;import com.example.jojo.learn.R;
import com.example.jojo.learn.utils.DP2PX;import java.util.ArrayList;
import java.util.List;/*** Created by JoJo on 2018/8/3.* wechat:18510829974* description: 曲线图/折线图*/public class LineChartView extends View {private Context mContext;//绘制坐标轴的画笔private Paint mAxisPaint;//绘制曲线的画笔private Paint mPaint;//绘制X轴上方的画笔private Paint mXAxisLinePaint;private Paint mPaintText;//向上的曲线图的绘制起点(px)private int startX;private int startY;//向下的曲线图的绘制起点(px)private int downStartX;private int downStartY;//上方Y轴每单位刻度所占的像素值private float YAxisUpUnitValue;//下方Y轴每单位刻度所占的像素值private float YAxisDownUnitValue;//根据具体传入的数据,在坐标轴上绘制点private Point[] mPoints;//传入的数据,决定绘制的纵坐标值private List<Integer> mDatas = new ArrayList<>();//Y轴刻度集合private List<Integer> mYAxisList = new ArrayList<>();//X轴刻度集合private List<String> mXAxisList = new ArrayList<>();//X轴的绘制距离private int mXAxisMaxValue;//Y轴的绘制距离private int mYAxisMaxValue;//Y轴刻度间距(px)private int yAxisSpace = 120;//X轴刻度间距(px)private int xAxisSpace = 200;//Y轴刻度线宽度private int mKeduWidth = 20;private float keduTextSize = 20;//刻度值距离坐标的padding距离private int textPadinng = 10;//Y轴递增的实际值private int yIncreaseValue;//true:绘制曲线 false:折线private boolean isCurve = true;private Rect mYMaxTextRect;private Rect mXMaxTextRect;private int mMaxTextHeight;private int mMaxTextWidth;public LineChartView(Context context) {this(context, null);}public LineChartView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public LineChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.mContext = context;initData();initView();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if (heightMode == MeasureSpec.AT_MOST) {heightSize = (mYAxisList.size() - 1) * yAxisSpace + mMaxTextHeight * 2 + textPadinng * 2;}if (widthMode == MeasureSpec.AT_MOST) {widthSize = startX + (mDatas.size() - 1) * xAxisSpace + mMaxTextWidth;}//保存测量结果setMeasuredDimension(widthSize, heightSize);}private void initView() {//初始化画笔mPaint = new Paint();mPaint.setColor(ContextCompat.getColor(mContext, R.color.color_efaf34));mPaint.setStrokeWidth(2);mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);//绘制X,Y轴坐标的画笔mAxisPaint = new Paint();mAxisPaint.setColor(ContextCompat.getColor(mContext, R.color.color_274782));mAxisPaint.setStrokeWidth(2);mAxisPaint.setAntiAlias(true);mAxisPaint.setStyle(Paint.Style.STROKE);//绘制坐标轴上方的横线的画笔mXAxisLinePaint = new Paint();mXAxisLinePaint.setColor(ContextCompat.getColor(mContext, R.color.color_274782));mXAxisLinePaint.setStrokeWidth(1);mXAxisLinePaint.setAntiAlias(true);mXAxisLinePaint.setStyle(Paint.Style.STROKE);//绘制刻度值文字的画笔mPaintText = new Paint();mPaintText.setTextSize(keduTextSize);mPaintText.setColor(ContextCompat.getColor(mContext, R.color.color_a9c6d6));mPaintText.setAntiAlias(true);mPaintText.setStrokeWidth(1);mYMaxTextRect = new Rect();mXMaxTextRect = new Rect();mPaintText.getTextBounds(Integer.toString(mYAxisList.get(mYAxisList.size() - 1)), 0, Integer.toString(mYAxisList.get(mYAxisList.size() - 1)).length(), mYMaxTextRect);mPaintText.getTextBounds(mXAxisList.get(mXAxisList.size() - 1), 0, mXAxisList.get(mXAxisList.size() - 1).length(), mXMaxTextRect);//绘制的刻度文字的最大值所占的宽高mMaxTextWidth = mYMaxTextRect.width() > mXMaxTextRect.width() ? mYMaxTextRect.width() : mXMaxTextRect.width();mMaxTextHeight = mYMaxTextRect.height() > mXMaxTextRect.height() ? mYMaxTextRect.height() : mXMaxTextRect.height();//指定绘制的起始位置startX = mMaxTextWidth + textPadinng + mKeduWidth;//坐标原点Y的位置(+1的原因:X轴画笔的宽度为2 ; +DP2PX.dip2px(mContext, 5)原因:为刻度文字所占的超出的高度 )——>解决曲线画到最大刻度值时,显示高度不够,曲线显示扁扁的问题startY = yAxisSpace * (mYAxisList.size() - 1) + mMaxTextHeight;if (mYAxisList.size() >= 2) {yIncreaseValue = mYAxisList.get(1) - mYAxisList.get(0);}//X轴绘制距离mXAxisMaxValue = (mDatas.size() - 1) * xAxisSpace;//Y轴绘制距离mYAxisMaxValue = (mYAxisList.size() - 1) * yAxisSpace;//坐标起始点Y轴高度=(startY+mKeduWidth)  下方文字所占高度= DP2PX.dip2px(mContext, keduTextSize)int viewHeight = startY + 2 * mKeduWidth + DP2PX.dip2px(mContext, keduTextSize);//viewHeight=121Log.e("TAG", "viewHeight=" + viewHeight);}/*** 根据传入的数据,确定绘制的点** @return*/private Point[] initPoint() {Point[] points = new Point[mDatas.size()];for (int i = 0; i < mDatas.size(); i++) {Integer ybean = mDatas.get(i);int drawHeight = (int) (startY * 1.0 - (ybean * yAxisSpace * 1.0 / yIncreaseValue));int startx = startX + xAxisSpace * i;points[i] = new Point(startx, drawHeight);}Log.e("TAG", "startX=" + startX + "---startY=" + startY);return points;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mPoints = initPoint();for (int i = 0; i < mYAxisList.size(); i++) {//Y轴方向递增的高度int yAxisHeight = startY - yAxisSpace * i;//绘制X轴和上方横线canvas.drawLine(startX - mKeduWidth, yAxisHeight, startX + (mDatas.size() - 1) * xAxisSpace, yAxisHeight, mXAxisLinePaint);//绘制左边Y轴刻度线
//                canvas.drawLine(startX, yAxisHeight, startX - mKeduWidth, yAxisHeight, mAxisPaint);//绘制文字时,Y轴方向递增的高度int yTextHeight = startY - yAxisSpace * i;//绘制Y轴刻度旁边的刻度文字值,10为刻度线与文字的间距mPaintText.setTextAlign(Paint.Align.RIGHT);canvas.drawText(mYAxisList.get(i) + "", (startX - mKeduWidth) - textPadinng, yTextHeight, mPaintText);}//绘制Y轴canvas.drawLine(startX, startY, startX, startY - mYAxisMaxValue, mAxisPaint);//绘制X轴下面显示的文字for (int i = 0; i < mXAxisList.size(); i++) {int xTextWidth = startX + xAxisSpace * i - mKeduWidth;//设置从起点位置的左边对齐绘制文字mPaintText.setTextAlign(Paint.Align.LEFT);Rect rect = new Rect();mPaintText.getTextBounds(mXAxisList.get(i), 0, mXAxisList.get(i).length(), rect);canvas.drawText(mXAxisList.get(i), startX - rect.width() / 2 + xAxisSpace * i, startY + rect.height() + textPadinng, mPaintText);}//连接所有的数据点,画曲线if (isCurve) {//画曲线drawScrollLine(canvas);} else {//画折线drawLine(canvas);}}/*** 绘制曲线-曲线图** @param canvas*/private void drawScrollLine(Canvas canvas) {Point startp;Point endp;for (int i = 0; i < mPoints.length - 1; i++) {startp = mPoints[i];endp = mPoints[i + 1];int wt = (startp.x + endp.x) / 2;Point p3 = new Point();Point p4 = new Point();p3.y = startp.y;p3.x = wt;p4.y = endp.y;p4.x = wt;Path path = new Path();path.moveTo(startp.x, startp.y);path.cubicTo(p3.x, p3.y, p4.x, p4.y, endp.x, endp.y);canvas.drawPath(path, mPaint);}}/*** 绘制直线-折线图** @param canvas*/private void drawLine(Canvas canvas) {Point startp;Point endp;for (int i = 0; i < mPoints.length - 1; i++) {startp = mPoints[i];endp = mPoints[i + 1];canvas.drawLine(startp.x, startp.y, endp.x, endp.y, mPaint);}}private void initData() {//外界传入的数据,即为绘制曲线的每个点mDatas.add(0);mDatas.add(10);mDatas.add(5);mDatas.add(20);mDatas.add(15);int[] mYAxisData = new int[]{0, 10, 20, 30, 40};for (int i = 0; i < mYAxisData.length; i++) {mYAxisList.add(mYAxisData[i]);}//X轴数据mXAxisList.add("01月");mXAxisList.add("02月");mXAxisList.add("03月");mXAxisList.add("04月");mXAxisList.add("05月");}/*** 传入数据,重新绘制图表** @param datas* @param yAxisData*/public void updateData(List<Integer> datas, List<String> xAxisData, List<Integer> yAxisData) {this.mDatas = datas;this.mXAxisList = xAxisData;this.mYAxisList = yAxisData;initView();postInvalidate();}
}

参考学习:
Android中moveTo、lineTo、quadTo、cubicTo、arcTo详解

Android 进阶自定义View(4)图表统计LineChartView曲线图的实现相关推荐

  1. Android 系统(264)---android进阶——自定义View

    android进阶--自定义View 软件架构 01.自定义View简介 - onMeasure,onDraw,自定义属性  https://www.jianshu.com/p/48944aad200 ...

  2. Android自定义睡眠下表统计图,Android 进阶自定义View(5)图表统计PieChartView圆饼图的实现...

    今天讲图表统计中比较常用的一个,像支付宝的月账单啥的,都是用圆饼图来做数据统计的,先看一下我最终实现的效果图: image.png 该效果实际上是两个实心圆叠加后的效果. image.png imag ...

  3. android 自定义viewgroup onmeasure,Android进阶——自定义View之View的绘制流程及实现onMeasure完全攻略...

    引言 Android实际项目开发中,自定义View不可或缺,而作为自定义View的一种重要实现方式--继承View重绘尤其重要,前面很多文章基本总结了继承View的基本流程:自定义属性和继承View重 ...

  4. Android进阶——自定义View之自己绘制彩虹圆环调色板

    引言 前面几篇文章都是关于通过继承系统View和组合现有View来实现自定义View的,刚好由于项目需要实现一个滑动切换LED彩灯颜色的功能,所以需要一个类似调色板的功能,随着手在调色板有效区域滑动, ...

  5. android 动态画直线,Android使用自定义view在指定时间内匀速画一条直线的实例代码...

    本文讲述了Android使用自定义view在指定时间内匀速画一条直线的实例代码.分享给大家供大家参考,具体如下: 1.效果图: 2.自定义view实现 public class UniformLine ...

  6. android 动态生成直线,Android使用自定义view在指定时间内匀速画一条直线的实例代码...

    本文讲述了Android使用自定义view在指定时间内匀速画一条直线的实例代码.分享给大家供大家参考,具体如下: 1.效果图: 2.自定义view实现 public class UniformLine ...

  7. android五子棋编程教程全集,android简单自定义View实现五子棋

    本文实例为大家分享了android自定义View实现五子棋的具体代码,供大家参考,具体内容如下 先说一下吧,android的自定义View就是自己实现一个类去继承View,实现其中的方法,这里面我最感 ...

  8. Android 中自定义View 裁剪扇形图片

    Android 中自定义View 裁剪扇形图片 当需要裁剪图片为扇形区域时,使用Canvas.clipPath(path)方法可以裁剪为扇形区域 ps:此方法会导致绘制图片边缘有锯齿,暂无解决方法(知 ...

  9. 【Android】自定义View、画家(画布)Canvas与画笔Paint的应用——画图、涂鸦板app的实现

    利用一个简单的画图app来说明安卓的图形处理类与自定义View的应用. 如下图,有一个供用户自己任意画图.涂鸦的app, 这里不做那么花俏了,仅提供黑白两色,但可以改变笔尖的粗细. 实质上这里的橡皮擦 ...

最新文章

  1. C++:MSVCRTD.lib(crtexe.obj) : error LNK2019: 无法解析的外部符号 _main,该符号在函数 ___tmainCRTStart...
  2. P1739表达式括号匹配
  3. [TJOI2011] 卡片(网络流 + 质因子优化建图)
  4. 年薪60w财务总监的加薪秘诀:从不用Excel做报表
  5. 计算机a类论文汇报,计算机学院2014年度发表和录用CCFA类、B类论文统计(初稿.xls...
  6. 女孩!自重!上海出租车司机的话
  7. KVM的安装和配置命令详解
  8. Linux嵌入式_详解从原理图到数据手册解析PWM蜂鸣器实现
  9. 风控体系建设、数字化转型、金融科技应用前,您是如何看待数据问题的?
  10. Memory Forensics (内存取证)
  11. python怎么做计算题_用python做算术题
  12. Pycharm正版2022.2.2 | 官方翻译插件更新tkk失败解决
  13. VM ware安装Cent OS系统并配置静态IP
  14. 微信小程序图片预览禁止保存
  15. zk - zookeeper主节点、从节点、客户端三者之间的交互
  16. java socketacceptor_rsocket-java小试牛刀
  17. CAD中添加A4边框、画箭头、斜线延长、添加特殊符号(矢量标记)
  18. soul网关mysql8_深度解析 Soul 网关——数据同步
  19. 天津大学计算机网络专业排名,2019计算机考研天津大学先进网络技术与应用重点实验室简介...
  20. 最全国外优秀技术网站推荐

热门文章

  1. java麦克风编程,java – Synch 2类似的音频输入(一个靠文件,一个靠麦克风)
  2. pandas UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xb1 in position 0: invalid start byte
  3. tensorflow tensor 张量 部分采样 切片和索引
  4. vs2019 MFC 中 cannot open include file 'afxres.h' 问题解决方法
  5. 实时目标检测--Pelee: A Real-Time Object Detection System on Mobile Devices
  6. Linux下的十个好用的命令工具:查看系统版本,显示目录的大小,查看硬盘HDD/SSD,硬盘测速,ssh时自动输入密码,查看程序的内存使用情况,查看I/O的速度,查看ssh密码错误日志,查找文件
  7. 码分复用的matlab仿真,基于matlab的多路时分复用仿真.doc
  8. redis有几种数据类型
  9. MySQL Order by 语句用法与优化详解
  10. android NinePatch图片制做