Android 自定义View -- 简约的折线图
转载请注明出处:http://write.blog.csdn.net/postedit/50434634
接上篇 Android 圆形百分比(进度条) 自定义view
昨天分手了,不开心,来练练自定义view麻痹自己,毕竟菜鸟只能靠不断练习提高。#程序员不应该有女朋友#
我们要实现的是一种只有来看趋势,不需要看具体数值,比较简约的折线图。比如下图这样的:
这个时候,一些比较优秀的第三方图表库如:MPChart 就显得比较臃肿了。所以我们需要自定义一个折线图。
老规矩,先来看最终的实现效果:
其实这种做的很简约,大概分三个步骤:
一、画坐标轴
二、画点
三、画线
那么我们开始吧Let's go (Let it go)。
设计一下大概需要的东西。首先把X轴和Y轴的数据存放在两个String[]里。
具体的点的位置用一个Map<Integer,Integer>来存放.
步骤:
一、新建一个类,取名为SimpleLineChart继承View 重写他的构造方法。这里为了简便,就不添加自定义属性了attr.xml。
<span style="font-size:18px;"> </span><span style="font-size:12px;"> public SimpleLineChart(Context context) {this(context, null);}public SimpleLineChart(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SimpleLineChart(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}</span>
二、测量大小。(继续偷懒,只支持EXACTLY,AT_MOST直接丢异常)
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if (widthMode == MeasureSpec.EXACTLY) {mWidth = widthSize;}else if(widthMode == MeasureSpec.AT_MOST){throw new IllegalArgumentException("width must be EXACTLY,you should set like android:width=\"200dp\"");}if (heightMode == MeasureSpec.EXACTLY) {mHeight = heightSize;}else if(widthMeasureSpec == MeasureSpec.AT_MOST){throw new IllegalArgumentException("height must be EXACTLY,you should set like android:height=\"200dp\"");}setMeasuredDimension(mWidth, mHeight);}
三、重点来了,开始draw。首先处理几种异常情况。当X轴或者Y轴为没有文字(也就是没有刻度的时候),抛出异常。
if(mXAxis.length==0||mYAxis.length==0){throw new IllegalArgumentException("X or Y items is null");}
当没有任何点的数据的时候,显示字符串提醒用户没有数据(实际上是往中心drawText)。
//画坐标线的轴Paint axisPaint = new Paint();axisPaint.setTextSize(mYAxisFontSize);axisPaint.setColor(Color.parseColor("#3F51B5"));
if (mPointMap == null || mPointMap.size() == 0) {int textLength = (int) axisPaint.measureText(mNoDataMsg);canvas.drawText(mNoDataMsg, mWidth/2 - textLength/2, mHeight/2, axisPaint);}
异常情况处理完了,开始上面三个步骤挨个画,首先来画个Y轴,计算每个刻度的间隔。他的值应该是mWidth / Y轴文字个数,然后用循环把每个刻度都画出来。再申请一个数组yPoints,存放每个Y刻度的具体坐标。
//画 Y 轴//存放每个Y轴的坐标int[] yPoints = new int[mYAxis.length];//计算Y轴 每个刻度的间距int yInterval = (int) ((mHeight - mYAxisFontSize - 2) / (mYAxis.length));//测量Y轴文字的高度 用来画第一个数Paint.FontMetrics fm = axisPaint.getFontMetrics();int yItemHeight = (int) Math.ceil(fm.descent - fm.ascent);Log.e("wing", mHeight + "");for (int i = 0; i < mYAxis.length; i++) {canvas.drawText(mYAxis[i], 0, mYAxisFontSize + i * yInterval, axisPaint);yPoints[i] = (int) (mYAxisFontSize + i * yInterval);}
我们运行一下,看到了如下效果:
需要注意的是,这里的坐标需要微调,大家多试一下。同理开始画X轴:
//画 X 轴//x轴的刻度集合int[] xPoints = new int[mXAxis.length];Log.e("wing", xPoints.length + "");//计算Y轴开始的原点坐标int xItemX = (int) axisPaint.measureText(mYAxis[1]);//X轴偏移量int xOffset = 50;//计算x轴 刻度间距int xInterval = (int) ((mWidth - xOffset) / (mXAxis.length));//获取X轴刻度Y坐标int xItemY = (int) (mYAxisFontSize + mYAxis.length * yInterval);for (int i = 0; i < mXAxis.length; i++) {canvas.drawText(mXAxis[i], i * xInterval + xItemX + xOffset, xItemY, axisPaint);xPoints[i] = (int) (i * xInterval + xItemX + axisPaint.measureText(mXAxis[i]) / 2 + xOffset + 10);
// Log.e("wing", xPoints[i] + "");}
注意这里X轴的y坐标就是画Y轴时候的最下面的文字(最后一个)的坐标,存成了xItemY。
之后我们来画点,这里我采用的方法是画圆。直接drawCircle。从map中取出所有点的对应i,j然后再从两个数组 xPoints[] yPoints[]取出真实的X,Y坐标,最后画出来
//画点Paint pointPaint = new Paint();pointPaint.setColor(mLineColor);Paint linePaint = new Paint();linePaint.setColor(mLineColor);linePaint.setAntiAlias(true);//设置线条宽度linePaint.setStrokeWidth(mStrokeWidth);pointPaint.setStyle(Paint.Style.FILL);for (int i = 0; i < mXAxis.length; i++) {if (mPointMap.get(i) == null) {throw new IllegalArgumentException("PointMap has incomplete data!");}//画点canvas.drawCircle(xPoints[i], yPoints[mPointMap.get(i)], mPointRadius, pointPaint);if (i > 0) {//画线canvas.drawLine(xPoints[i - 1], yPoints[mPointMap.get(i - 1)], xPoints[i], yPoints[mPointMap.get(i)], linePaint);}}
上面画完点之后开始画线drawLine,参数是前一个点的坐标,和后一个点的坐标。挨个画出来。
这时候我们的最复杂的绘制就完成了。接下来来加入一点功能:参数的设置。
/*** 设置map数据* @param data*/public void setData(HashMap<Integer,Integer> data){mPointMap = data;invalidate();}/*** 设置Y轴文字* @param yItem*/public void setYItem(String[] yItem){mYAxis = yItem;}/*** 设置X轴文字* @param xItem*/public void setXItem(String[] xItem){mXAxis = xItem;}public void setLineColor(int color){mLineColor = color;invalidate();}
以上代码很简单 我就不多说了。整个View完工,接下来介绍如何使用。
使用:
和普通的View一样,我们直接在XML布局文件中加入SimpleLineChart,注意不要忘记包名。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:paddingBottom="@dimen/activity_vertical_margin"tools:context="com.wingsofts.simplelinechart.MainActivity"><com.wingsofts.simplelinechart.SimpleLineChartandroid:id="@+id/simpleLineChart"android:layout_width="400dp"android:layout_height="200dp" />
</RelativeLayout>
然后在Activity中findviewbyid,给他设置X轴的文字 Y轴的文字 还有数据源
public class MainActivity extends AppCompatActivity {private SimpleLineChart mSimpleLineChart;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mSimpleLineChart = (SimpleLineChart) findViewById(R.id.simpleLineChart);String[] xItem = {"1","2","3","4","5","6","7"};String[] yItem = {"10k","20k","30k","40k","50k"};if(mSimpleLineChart == null)Log.e("wing","null!!!!");mSimpleLineChart.setXItem(xItem);mSimpleLineChart.setYItem(yItem);HashMap<Integer,Integer> pointMap = new HashMap();for(int i = 0;i<xItem.length;i++){pointMap.put(i, (int) (Math.random()*5));}mSimpleLineChart.setData(pointMap);}
}
简单的几步,就可以得到预览图的效果了!是不是很好玩!觉得好的话评论一下,star一下。祭奠我死去的爱情。
项目下载地址(求关注 求星星 ):点击打开链接
下一篇来一个比较炫 比较复杂的view 自定义仪表盘 :时尚自定义仪表盘
转载于:https://www.cnblogs.com/muyuge/p/6333560.html
Android 自定义View -- 简约的折线图相关推荐
- Android自定义View——实现理财类APP七日年化收益折线图效果
这段时间的自定义View学习,学会了绘制柱状图.绘制折线图.绘制进度控件,那我们今天就来聊聊另外一种自定义的View,这就是我们常见的七日年化收益折线图效果.先看看长什么样. 这就是效果图了,元素相对 ...
- android 轨迹生成图,Android自定义View实现公交成轨迹图
本文实例为大家分享了Android自定义View实现公交成轨迹图的具体代码,供大家参考,具体内容如下 总体分析下:水平方向recyclewview,item包含定位点,站台位置和站台名称. 下面看实现 ...
- Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图)
Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图) 前言 自定义View的关键点 绘制多边形 结尾 前言 刚开始学习自定义view,简单完成了一个属性分布器,可以实现三条到八条 ...
- android自定义View: 饼状图绘制(四)
本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 画矩 ...
- android 自定义view: 蛛网/雷达图(三)
本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 蛛网 ...
- 一篇文章带你走近Android自定义view
系列文章目录 一篇文章带你走近Android自定义view 文章目录 系列文章目录 前言 一.为什么要自定义view 二.先看看一个超级简单的自定义view(三个构造函数) 三.了解手机的坐标系 四. ...
- Android自定义View之Paint绘制文字和线
Android自定义View系列 Android自定义View注意事项 Android自定义View之图像的色彩处理 Android自定义View之Canvas Android自定义View之轻松实现 ...
- Android 自定义View —— Canvas
上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...
- Android自定义View:ViewGroup(三)
自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...
最新文章
- Windows Forms高级界面组件-快捷菜单
- crontab 时间参数解释
- CentOS下创建配置RAID1
- 基于用户击键特征的身份鉴别系统
- n皇后问题(在棋盘上找方案)
- Flask部署工具的安装与使用
- 施耐德 m340 编程手册_施耐德PLC漏洞历险记
- C中使用errno查看函数调用的错误
- Chrome浏览器离线安装包下载方法
- 企业如何做好服务器防护45.113.201.1
- 手把手教你在 Vue 中使用 JSX,不怕学不会!【建议收藏】
- UltraCompare Crack,重复文件查找器
- laravel 路由_简单的Laravel路由
- 经典语录用心记忆,总有一句让你受益匪浅!
- 多点相册--将手机的照片和视频备份到电脑的工具
- android播放器概述,【Android】 从头搭建视频播放器(1)——概述
- [RL robotic 环境] - [Robosuite](1)
- 计算机视觉学习路线—计算机视觉入门必读的20本书
- tensorflow 常遇函数
- 3D数学系列之——从“蒙的挺准”到“蒙的真准”解密蒙特卡洛积分!