失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android自定义View之年度账单曲线图

Android自定义View之年度账单曲线图

时间:2020-04-17 21:13:06

相关推荐

Android自定义View之年度账单曲线图

前言

一说到折线图曲线图,我马上就想到了GitHub上的MPAndroidChart,扩展功能强大,本来想用,不过我转念一想,就一个年度账单,自己写不是几百行代码就搞定了?于是我开始了。先看一下设计图

思考

先考虑需要几种画笔Paint:曲线、字体、网格、背景色、详情背景色再考虑几个重要的变量:折线单位高度差、横向和纵向Item的高宽度、当年的最高用量、当年的最低用量需要确定的数据源:当前年份的水表数据集合需要计算每个月点的坐标位置集合List<PointF>横向可滑动、选中的点pointFSelected以及点击范围的判定,在每个月峰值某个范围内允许点击。

必须先了解的知识点

Canvas坐标系与绘图坐标系:简单点说Canvas坐标系唯一不变,平常我们使用绘图坐标系(例如canvas的drawLine)。Canvas的save和restore方法:了解了绘图坐标系,应该很好了解,比如我先sava一下(保存当前矩阵),然后调用canvas的旋转,然后使用Paint去绘制,绘制完了,调用restore回到之前没有旋转的画板继续画。VelocityTracker:速度追踪器,主要用跟踪触摸屏事件(flinging事件和其他gestures手势事件)的速率。ViewConfiguration:包含用于UI的标准常量的方法,用于超时,大小和距离。用来判断是否是抛动。Paint使用PathEffect画出虚线

//两个值分别为循环的实线长度、空白长度float[] f = {dp2pxF(5f,context), dp2pxF(2f,context)};PathEffect pathEffect = new DashPathEffect(f, 0);paint.setPathEffect(pathEffect);/*** dp转pxF*/public static float dp2pxF(float dpValue,Context context) {return TypedValue.applyDimension(PLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());}

画渐变背景:通过设置画笔来实现。

/*** 渐变背景画笔*/private Paint backGroundPaint;backGroundPaint=new Paint(Paint.ANTI_ALIAS_FLAG);//设置抗锯齿backGroundPaint.setAntiAlias(true);//为Paint设置渐变LinearGradient linearGradient=new LinearGradient(pointFMaxCurve.x,pointFMaxCurve.y,pointFList.get(0).x,viewHeight-itemWidth,new int[]{0xFFE1F1FF,0xFFEFF7FF,0xFFFAFCFF},null, Shader.TileMode.CLAMP);backGroundPaint.setShader(linearGradient);

text宽度的自适应:因为数据变化大,所以详情框坐标确认就需要获取text的宽度。思路是这样子:数据都是空字符串时背景框的宽度加上获取text的宽度就是上图用量读数弹框的宽度了。

/*** 精确计算文字宽度** @param paint* @param str* @return 文字长度,像素*/public static int getTextWidth(Paint paint, String str) {int iRet = 0;if (str != null && str.length() > 0) {int len = str.length();float[] widths = new float[len];paint.getTextWidths(str, widths);for (int j = 0; j < len; j++) {iRet += (int) Math.ceil(widths[j]);}}return iRet;}

Path常用Api:如cubicTo画三次贝塞尔曲线、reset清除当前路径、moveTo移动到某个坐标但是移动过程没有痕迹。

动手

先定义常量

/*** 折线画笔*/private Paint brokenLinePaint;/*** 坐标画笔*/private Paint coordinatePaint;/*** 圆点画笔*/private Paint circlePaint;/*** 文字画笔(x、y轴)*/private Paint textPaint;/*** 文字画笔(水表详情)*/private Paint textPaintDetail;/*** 渐变背景画笔*/private Paint backGroundPaint;/*** 详情背景画笔*/private Paint backGroundDetailPaint;/*** 背景颜色*/private int backGroundColor= Color.WHITE;/*** 文字画笔颜色* 默认:黑色*/private int colorTextPaint= Color.BLACK;/*** 折现画笔颜色* 默认:蓝色 0xFF1281FD*/private int colorBrokenLinePaint=0xFF1281FD;/*** 坐标画笔颜色* 默认 灰色 0xFFEEEEEE*/private int colorCoordinatePaint=0xFFEEEEEE;/*** 详情文字背景颜色*/private int colorDetailTextBg=0x66000000;/*** 文字大小* 默认11sp*/private float textSize;/*** 水表详情文字大小* 默认10sp*/private float textSizeDetail;/*** 单位高度差* 默认:itemWidth/unitYItem*/private float unitVerticalGap;/*** itemWidth对应的用水量(吨)*/private int unitYItem;/*** Y方向有数据的Item个数* 默认4个*/private static final int itemYSize=4;/*** 折线图左和下的间距,同横纵单位间隔* 默认:42dp*/private int defaultPadding;private int itemWidth;/*** 控件期望高度* 默认为8个itemWidth*/private int expectViewHeight;/*** 折线点的半径(默认2.5dp的像素)*/private float pointRadius;private int viewWidth;private int viewHeight;private int screenWidth;private int screenHeight;/*** 曲线路径*/private Path curvePath;/*** 水表详情背景路径*/private Path WaterDetailBgPath;/*** 水表详情背景图范围*/private RectF rectF;/*** 最多用量*/private float maxDosage;/*** 最少用量*/private float minDosage;/*** 数据列表data*/private List<WaterAndElectricMeterDetail> list=new ArrayList<>();/*** 每个月的坐标点集*/private List<PointF> pointFList=new ArrayList<>();/*** 选中的那个点*/private PointF pointFSelected=null;/*** 速度追踪器*/private VelocityTracker velocityTracker;/*** 关于UI的标准常量*/private ViewConfiguration viewConfiguration;/*** Scroller*/private Scroller scroller;

重写View的构造方法,使其最终指向带有3个参数的构造方法

this(context,null);this(context, attrs,0);super(context, attrs, defStyleAttr);

构造方法内初始化数据,此步代码略重写onMeasure方法,确定宽高,因为高度一开始就是固定的,有需求的小伙伴可以在方法内确定高度。重写onDraw方法

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//画渐变蓝色背景drawBackBlue(canvas);//画坐标drawAxis(canvas);//画曲线drawCurve(canvas);//画小圆点和虚线drawPointsAndLine(canvas);//画水表详情框drawWaterDetailsText(canvas);}

写一个公开方法,用于设置元数据

/*** 公开方法,用于设置元数据*/public void setData(List<WaterAndElectricMeterDetail> data) {if (data == null) {return;}//数据清理list.clear();pointFList.clear();pointFSelected=null;this.list = data;//计算单位高度差calculateGap();//获取数据点集initPointFData();invalidate();}

附上GitHub上Demo地址:/PengHaiZhuo/MyMiUiWeatherDemo

后话

因需求简单,所以很多变量值都写死了,有兴趣的朋友可以自行修改或者通过定义自定义属性来优化。

非常感谢这个博主分享,提供了思路,参考:/ccy0122/article/details/76464825

如果觉得《Android自定义View之年度账单曲线图》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。