前言
最近利用空闲时间学习了自定义view的一些知识,为了巩固,写了一个小东西,顺便分享出来,下面话不多说了,来一起看看详细的介绍吧。
简介
一个多边形统计图。边数,每个方向的值,每个点的文字等等都是可以设置的。
下面就来分析一下这个自定义view
这个vIEw由以下几个部分组成
M层N边形 中心到各顶点的连线 填充区域 文字@OverrIDe protected voID onDraw(Canvas canvas) { if (!canDraw()) { return; } canvas.translate(wIDth / 2,height / 2); computeMaxPoint(); drawpolygon(canvas); drawline(canvas); drawArea(canvas); drawText(canvas); }
我们一步一步来说明
绘制多边形
绘制多边形主要用到的是Path这个东西。具体的思路就是先计算好每个点的位置,同Path的lineto方法连接起来,然后绘制。
我的做法是先算出最大的半径(再之后还会用到,建议单独存起来),然后根据所在层数来计算每一层的半径,利用cos函数各sin函数计算出每一层各顶点的位置。
计算最大半径并且保存顶点
@OverrIDe protected voID onSizeChanged(int w,int h,int olDW,int oldh) { wIDth = w; height = h; maxRadius = (float) ((wIDth / 2) * 0.8); postInvalIDate(); }
/* 计算最大半径,之后的位置都是基于最大半径的比例 */ public voID computeMaxPoint() { maxPointXList = new ArrayList<>(); maxPointYList = new ArrayList<>(); for (int i = 0; i < eageCount; i++) { float currentAngle = i * angle - 90; float currentX = (float) (maxRadius * Math.cos((currentAngle / 180) * Math.PI)); float currentY = (float) (maxRadius * Math.sin((currentAngle / 180) * Math.PI)); maxPointXList.add(currentX); maxPointYList.add(currentY); } }
注意:cos和sin都是按照弧度制计算的,要换算。
这里解释一下为currentAngle什么要减去90度
按照androID的坐标系,如果不减去90度直接乘上cos的话,第一个顶点会默认在中心的右侧,而一般的认知是第一个点在正上方,所以减去90度
按照比例和层数边数绘制多边形
/* 绘制多边形和每一层 */ private voID drawpolygon(Canvas canvas) { Path path = new Path(); for (int i = 0; i < loopCount; i++) { path.reset(); //依据最大半径和角度来判断每一层点的位置 float rate = computerate(i + 1,loopCount); for (int j = 0; j < eageCount; j++) { float currentX = maxPointXList.get(j) * rate; float currentY = maxPointYList.get(j) * rate; if (j == 0) { path.moveto(currentX,currentY); } else { path.lineto(currentX,currentY); } } path.close(); canvas.drawPath(path,eagePaint); } }
代码还是很容易的吧,要是看不懂的话自己动手算算就知道了,很容易计算各个点的位置。
绘制连线
由于之前保存了顶点的坐标,这个就很容易了
/* 画出从中心向各顶点的连线 */ private voID drawline(Canvas canvas) { Path path = new Path(); for (int i = 0; i < eageCount; i++) { path.reset(); path.lineto(maxPointXList.get(i),maxPointYList.get(i)); canvas.drawPath(path,eagePaint); } }
绘制覆盖区域
这个原理其实和绘制多边形是一样的,就是对顶点坐标乘的比例发生了变化。每个方向的数值是由用户传递进来的。
/* 绘制个方向值覆盖的区域 */ private voID drawArea(Canvas canvas) { Path path = new Path(); //原理就是用path根据各方向值创建一个封闭的区域,然后填充 for (int i = 0; i < eageCount; i++) { float rate = pointValue.get(i); float currentX = maxPointXList.get(i) * rate; float currentY = maxPointYList.get(i) * rate; if (i == 0) { path.moveto(currentX,currentY); } else { path.lineto(currentX,currentY); } } path.close(); canvas.drawPath(path,areaPaint); }
绘制文字
说实话,前面的没有什么难点,但是唯独绘制文字有许多麻烦事。主要是文字是默认自左向右的,最上面和最先面的文字倒是没啥,左侧和右侧的文字就会出现问题了,文字会绘制到多边形上,看起来特别难受。这里我的解决办法就是前面图中看到的,让字跟着多边形的顶点位置一起旋转。
/* 绘制文字 */ private voID drawText(Canvas canvas) { if (pointname == null) { return; } //绘制文字的难点在于无法最好的适配屏幕的位置,会发生难以控制的偏倚 for (int i = 0; i < pointname.size(); i++) { //解决办法就是让文字在不同的角度也发生旋转,并且在x轴上减去一定的数值来保证正确的位置 float currentAngle = i * angle; //180度需要也别的处理,让它正着显示,不然就是倒着的 if (currentAngle == 180) { float currentX = maxPointXList.get(i) * 1.1f; float currentY = maxPointYList.get(i) * 1.1f; canvas.drawText(pointname.get(i),currentX - (textPaint.getTextSize() / 4) * (pointname.get(i).length()),currentY,textPaint); } else { canvas.save(); float currentX = maxPointXList.get(0) * 1.1f; float currentY = maxPointYList.get(0) * 1.1f; //旋转画布,达到旋转文字的效果 canvas.rotate(currentAngle); canvas.drawText(pointname.get(i),textPaint); canvas.restore(); } } }
到这里,整个组件就绘制完成了
额外的属性
如果单纯只是想画出这个组件来,其实没啥难度。我们可以在加一些别的东西让他更加实用。
动画效果
利用属性动画的知识,我们可以做到让中间的填充区域慢慢的扩散出来。原理也简单,就是把0到1用属性计算展开,当做一个演化的比例,让各个方向的值乘上这个数值,绘制一个比原先覆盖区域小的区域就可以了。
/* 用属性动画绘制组件 */ public voID draw() { if (canDraw()) { final float[] trueValues = pointValue.toArray(new float[pointValue.size()]); ValueAnimator valueAnimator = ValueAnimator.offloat(0,1); valueAnimator.setDuration(1000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) { float rate = animation.getAnimatedFraction(); for (int i = 0; i < pointValue.size(); i++) { pointValue.set(i,trueValues[i] * rate); } invalIDate(); } }); valueAnimator.start(); } }
定义xml属性
我们正常使用系统组件的时候都会写一大堆的xml来控制我们组件的属性,自定义view也可以尝试这些
首先在value下创建atts文件
然后指定你想要的属性名称和类型
再然后就是让atts和我们的vIEw联系上。这个也简单,仔细观察VIEw的构造方法中的参数,有这么一个玩意 AttributeSet attrs
public polygonVIEw(Context context,@Nullable AttributeSet attrs) { super(context,attrs); init(context,attrs); } public polygonVIEw(Context context,@Nullable AttributeSet attrs,int defStyleAttr) { super(context,attrs,defStyleAttr); init(context,attrs); }
它就是联系xml和vIEw的纽带
xmlns:app="http://schemas.androID.com/apk/res-auto"<com.totoro.xkf.polygonvIEw.polygonVIEw androID:ID="@+ID/pv_polygon_vIEw" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" app:areacolor="@androID:color/holo_blue_light" app:eagecolor="@androID:color/black" app:eageCount="6" app:loopCount="4" app:textcolor="@androID:color/black" />
public voID init(Context context,AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.polygon); initPaint(); setTextcolor(typedArray.getcolor(R.styleable.polygon_textcolor,color.BLACK)); setLoopCount(typedArray.getInteger(R.styleable.polygon_loopCount,0)); setEageCount(typedArray.getInteger(R.styleable.polygon_eageCount,0)); setAreacolor(typedArray.getcolor(R.styleable.polygon_areacolor,color.BLUE)); setEagecolor(typedArray.getcolor(R.styleable.polygon_eagecolor,color.GRAY)); typedArray.recycle(); }
xmlns:app=http://schemas.android.com/apk/res-auto
这个东西不能忘了
快速使用
感谢你看到这里,如果你想使用这个组件但是不想自己写的话欢迎访问
项目Github里面有讲如何添加依赖,导入组件
如果能帮到你的话,不胜荣幸!!!
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对编程小技巧的支持。
总结以上是内存溢出为你收集整理的Android自定义View实现多边形统计图示例代码全部内容,希望文章能够帮你解决Android自定义View实现多边形统计图示例代码所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)