在之前的许多项目中,在很多地方都需要有统计图来展示数据,其中用的比较多的就是折线图,本来一直想自己画的,无奈每次项目都是赶时间,基本都是在网上找个别人写好的,能基本满足需求的,就直接拿来用了。现在稍微有点时间了,准备自己着手写一个,顺便也好重新回顾一下自定义view 的整个流程(太久没写自义定view相关东西,都感觉好多东西忘了)。

对于自定义view来说,其中最重要的两个方法就是onMeasure()和onDraw(),onMeasure()方法负责计算view自身的大小,onDraw方法就是负责绘制界面,画出你想要的东西,闲话不多说,开始动手撸代码。

先继承view这个不多说,代码如下:

public class LineChartView extends View {private final int defaultWidth = 500;private final int defaultHeight = 500;public LineChartView(Context context) {super(context);}public LineChartView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasuer(widthMeasureSpec,heightMeasureSpec);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);}
}

上面的代码可以看到,主要是构造方法和几个要用到的核心方法,构造方法不多说,下面来看onMeasure()这个方法,有些时候可以不用重写,但是大部分时候还是要重写一下,里面的关键代码:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = 0;// 获取宽度测量规格中的mode,MeasureSpec类中的三个Mode常量值:UNSPECIFIED,EXACTLY,AT_MOSTint widthMode = MeasureSpec.getMode(widthMeasureSpec);// 获取宽度测量规格中的sizeint widthSize = MeasureSpec.getSize(widthMeasureSpec);if(widthMode == MeasureSpec.EXACTLY){         //EXACTLYwidth = widthSize;}else if(widthMode == MeasureSpec.AT_MOST){   //AT_MOSTwidth = Math.min(defaultWidth,widthSize);}else{                                       //UNSPECIFIED width = defaultWidth;}int height = 0;int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if(heightMode == MeasureSpec.EXACTLY){height = heightSize;}else if(heightMode == MeasureSpec.AT_MOST){height = Math.min(defaultHeight, heightSize);}else{height = defaultHeight;}setMeasuredDimension(width, height);   //设置大小}

如上所示,主要就是三种测量的mode:EXACTLY,AT_MOST,UNSPECIFIED。

EXACTLY:父容器决定其大小,忽略view本身的大小,当width或height设为固定值或者match_parent时,为EXACTLY模式;

AT_MOST:view最大可以达到的大小,当width或heifht设置为wrap_content时,为AT_MOST模式;

UNSPECIFIED:这个模式下,父容器不对view施加任何约束,view想多大就多大。

view 的大小写好之后,下面要定义一些属性,并初始化,代码如下:

    private int top, left, right, bottom;  //坐标轴上下左右上个点private Paint axisPaint;  //坐标轴画笔private Paint linePaint;  //折线图画笔private Paint scalePaint; //刻度线画笔private Paint textPaint;  //文字画笔private Path axisPath;    //坐标轴线条private Path linePath;    //折线图线条private Path scalePath;   //刻度线线条

上面主要是定义了要用到的画笔paint和路径path,定义这么多种类的画笔和线条为了下面容易区分开来,下面就是初始化工作:

private void init(){axisPath = new Path();linePath = new Path();scalePath = new Path();axisPaint = new Paint();axisPaint.setColor(Color.BLUE);             //画笔的颜色axisPaint.setStrokeWidth(4);                //描边的宽度axisPaint.setStyle(Paint.Style.STROKE);     //画笔的风格axisPaint.setAntiAlias(true);               //是否抗锯齿linePaint = new Paint();linePaint.setColor(Color.BLACK);linePaint.setStrokeWidth(5);linePaint.setStyle(Paint.Style.STROKE);linePaint.setAntiAlias(true);scalePaint = new Paint();scalePaint.setColor(Color.LTGRAY);scalePaint.setStrokeWidth(2);scalePaint.setAntiAlias(true);scalePaint.setStyle(Paint.Style.STROKE);//设置PathEffect可以画出一些不同线条的效果,这边是画了一条虚线scalePaint.setPathEffect(new DashPathEffect(new float[]{8f,2f}, 1f)); textPaint = new Paint();textPaint.setColor(Color.BLACK);textPaint.setStyle(Paint.Style.FILL_AND_STROKE);textPaint.setAntiAlias(true);textPaint.setStrokeWidth(1);textPaint.setTextSize(Util.sp2px(context,10));}

上面这些关于画笔的属性设置不多做解释,属性很多,常用的就上面这些,下面还有做的是确定整个坐标轴的坐标:

    @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);left = Util.dip2px(context, 40);top = Util.dip2px(context, 20);right = w - Util.dip2px(context, 10);bottom = h - Util.dip2px(context, 20);}

在onSizeChanged()方法中,确定坐标轴四个点的位置。下面我就可以先尝试一下画出坐标轴:

    @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);axisPath.moveTo(left, top);axisPath.lineTo(left, bottom);axisPath.lineTo(right, bottom);canvas.drawPath(axisPath, axisPaint);}

OnDraw()方法也是在我们自定义view时最重要的一个方法,view给我们提供了一个画布canvas,以便我们在这个画布上来画出我们想要的东西。Canvas这个类,为我们提供了相当丰富的Api,这么多的方法我们是没必要去记的,直接打个draw出来,AndroidStudio就能把canvas提供的方法都展现出来,很多方法看名称我就能大概知道是干嘛的了。在OnDraw()方法中,简单的画了一个坐标轴:
                                                           

接着我要先定义一个Point类,作为走势图的数据类,如下:

public class Point  implements Comparable<Point>{private float income;private String date;...
}

然后在自定义view中加个set方法:

private List<Point> points;
public void setPoints(List<Point> points) {this.points = points;invalidate();}

上面的就不多说了,那么接下来我要根据传入的数据来画刻度线,代码如下:

    private void drawScaleLine(Canvas canvas){float specX = (right - left) / (points.size() - 1);float xDivider = specX * 4;float yDivider = (bottom - top) / 5;//x轴刻度线for(int x = 1;x <= points.size()/4;x++){scalePath.moveTo(left + xDivider * x, bottom);scalePath.lineTo(left + xDivider * x, top);}//y轴刻度线for(int y=1;y<=5;y++){scalePath.moveTo(left, bottom - yDivider * y);scalePath.lineTo(right, bottom - yDivider * y);}canvas.drawPath(scalePath, scalePaint);//绘制x轴文字for(int x1 = 0;(x1 * 4) < points.size();x1++){canvas.drawText(points.get(x1 * 4).getDate(), left + xDivider * x1 - 35,bottom + 40, textPaint);}//绘制y轴数值Point p = Collections.max(points);float avIncome = p.getIncome() / 5;for(int y1 = 1;y1 <=5 ;y1++){String income = new DecimalFormat("0.00").format(avIncome * y1);float textWidth = textPaint.measureText(income);canvas.drawText(income,left - textWidth - 10,bottom - yDivider * y1 + 7, textPaint);}}

上面主要是对x和y轴进行平均分,我这边对x和y轴的处理有点不一样,x轴的刻度是根据点的个数先均分,然后4个点一个刻度;y轴是将点的最大值作为y轴最大值,然后将y轴均分成5份,然后取出这些刻度线的坐标,进行画线,x和y轴的文字,就根据各刻度线坐标稍加一点偏移画上去。现在这些都是写死的,后面可以慢慢去完善,将x和y轴的分割更灵活一点,好适应更多需求。加入上面一段代码后,现在我们的坐标系如下:

这边是随机出来的25个坐标点:

List<Point> points = new ArrayList<>();for(int i=1;i<=25;i++){Point point = new Point();float income = (float) Math.random() * 100;point.setIncome(income);point.setDate("06-" + i);points.add(point);}lineChartView.setPoints(points);

接下来要做的就是,描点连线了,代码如下:

private void drawDataLine(Canvas canvas){Point p = Collections.max(points);float specY = (bottom - top) / p.getIncome();float specX = (right - left) / (points.size() - 1);for(int i=0;i<points.size();i++){Point point = points.get(i);float x = i*specX + left;float y = bottom  - point.getIncome() * specY;if(i == 0){linePath.moveTo(x, y);}else{linePath.lineTo(x, y);}//在每个坐标点上画个小圆点canvas.drawCircle(x, y, 3f, linePaint);}canvas.drawPath(linePath, linePaint);}

取坐标点的思路和上面画刻度线的思路基本一样,最后加上偏移就行了,最后运行图如下:

到这,走势图的基础版本差不多了,其实就是在计算坐标点的时候稍微有点复杂,这个需要自己去控制。

一哈~

后面对这个基础版本进行改造,升级,以满足更多需求。

自定义view画走势图(一)相关推荐

  1. 各种金融类的自定义 View,基金走势图、分时图、蜡烛图、各种指标等,一步一步构建庞大的基金自定 View...

    inancialCustomerView 项目地址:Tophold/FinancialCustomerView  简介:各种金融类的自定义 View,基金走势图.分时图.蜡烛图.各种指标等,一步一步构 ...

  2. android自定义View: 饼状图绘制(四)

    本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 画矩 ...

  3. android xml画圆,Android自定义View画圆功能

    本文实例为大家分享了Android自定义View画圆的具体代码,供大家参考,具体内容如下 引入布局 xmlns:tools="http://schemas.android.com/tools ...

  4. 安卓自定义View画钟实现转动

    1.自定义View画钟实现转动 这个和画圆差不多,不过只是价格分针,并实现其转动,要加一个线程.代码如下: Java类中的代码 package com.example.ll.canvas;import ...

  5. 用python画走势图

    可以使用Python中的Matplotlib库来画走势图. 首先,需要安装Matplotlib库.可以使用以下命令进行安装: pipinstall matplotlib

  6. 自定义view 太极八卦图

    自定义view 太极八卦图

  7. android编程绘制扇形,分析实现Android自定义View之扇形图

    继承View基类,画了这样的扇形图 粗糙的样子^_^ 直接来步骤吧 1.分析 自定义View需要认真的分析下,里面还是会用到一些数学知识 首先是扇形该怎么表现 1. 扇形的外观是个圆弧,而且是围绕一个 ...

  8. android 自定义view: 蛛网/雷达图(三)

    本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 蛛网 ...

  9. Android 自定义View 画圆(奥运五环)

    效果图 前言 你会画画吗?你会写代码吗?你会用代码画画吗? 正文 自定义View,实际开发过程中,因为涉及用户体验的时候,UI通常会弄一些骚操作出来,这个时候就有两个选择,你是用GIF呢?还是自己自定 ...

最新文章

  1. centos7 安装 python3
  2. 看动画学算法之:二叉搜索树BST
  3. 虚线 实现_redis跳跃表实现
  4. GoldenGate 12.3微服务架构与传统架构的区别
  5. linux开启mysql外链,Linux中开启MySQL远程访问功能
  6. gitlab搭建与使用
  7. 一些Linux历史的小散
  8. Java基础学习总结(61)——Java项目开发要注意的60个问题
  9. 惠普微型计算机怎么装机,台式小机惠普电脑怎么装系统
  10. 人工智能之-产生式系统
  11. Apple ID Your Account Cannot Be Created at This Time
  12. 微信小程序创建过程(具体步骤)
  13. 加州房价预测数据预处理
  14. 三年前,故事这样开始 三年后,故事这样延续---2015年终总结
  15. 乓乓响再次冲刺香港上市,黄建义、张卫平夫妇套现约2130万元
  16. chrome无痕模式可访问但正常模式访问不了
  17. step5. 安装Eclipse4.7(Oxygen)+如何优雅书写多个工具的PATH路径(转)
  18. 加速电脑浏览器下载和加载网页速度
  19. 我常用的软件,无广告,全干货。
  20. 第二届传智杯初赛B题 贪心算法橙子问题

热门文章

  1. 吴永辉5-6 (归并排序,模拟优先队列,set,哈希存储)
  2. 自定义异常,throw,throws和带你去旅行
  3. 《Adobe After Effects CS6完全剖析》——弹力球教给你些什么
  4. vue-pdf不显示盖章_如何规避OFFICE与WPS排版显示不一致
  5. C语言之精华总结(下)
  6. 帝国时代2高清版秘籍
  7. Linux用户密码文件/etc/shadow相关
  8. winscp如何连接安卓手机_WinSCP安卓版下载_WinSCP网站FTP管理软件手机版下载_软件侠下载站...
  9. 如何通过Java代码在Word中创建可填充表单
  10. pycharm如何找到python解释器_pycharm配置远程python解释器