Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图)
Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图)
- 前言
- 自定义View的关键点
- 绘制多边形
- 结尾
前言
刚开始学习自定义view,简单完成了一个属性分布器,可以实现三条到八条属性的分布图,依次是三角形到正八边形,可以在xml和代码中设置属性个数和显示层数,并没有用到什么复杂的知识点,简单的canvas绘制就可以完成,复杂一点点可能就是绘制坐标的计算,我是使用的kotlin进行的编写,也是一个小新手,没使用太多的简化写法。这篇文章主要做一个记录,如果能帮到大家也自然是极好。废话不多说,开始我的炸弹秀,不好意思跑题了,开始
自定义View的关键点
自定义View分为两种,一是自定义单一View(不包含子View),二是自定义ViewGroup(包含子View),本例是一个单一的View。
首先写一个类继承于View,有四个构造方法
class MyView : View {// 自定义View有四个构造函数// 如果View是在Java代码里面new的,则调用第一个构造函数constructor(context: Context) : super(context)// 如果View是在.xml里声明的,则调用第二个构造函数// 自定义属性是从AttributeSet参数传进来的constructor(context: Context, attributeSet: AttributeSet) : this(context, attributeSet, 0)// 不会自动调用// 一般是在第二个构造函数里主动调用// 如View有style属性时// 这个方法里的内容是自定义属性的内容constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr) {val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.MyView)if (typedArray != null) {//MyView_line_color线条颜色lineColor = typedArray.getColor(R.styleable.MyView_line_color, Color.BLACK)//MyView_line_size线条宽度paintSize = typedArray.getDimension(R.styleable.MyView_line_size, 5f)//MyView_floor_count绘制层数(不得小于各元素分配的比例)floorCount = typedArray.getInt(R.styleable.MyView_floor_count, 3)//MyView_angle_count绘制元素个数angleCount = typedArray.getInt(R.styleable.MyView_angle_count, 3)//MyView_text_color文本颜色textColor = typedArray.getColor(R.styleable.MyView_text_color, Color.BLACK)//MyView_text_size文本大小textSize = typedArray.getDimension(R.styleable.MyView_text_size, 18f.dp)//MyView_area_color覆盖区域颜色areaColor = typedArray.getColor(R.styleable.MyView_area_color, ContextCompat.getColor(context, R.color.trared))//MyView_default_text_padding默认文本与图形间距defaultTextPadding = typedArray.getDimension(R.styleable.MyView_default_text_padding, 20f)}}//API21之后才使用// 不会自动调用// 一般是在第二个构造函数里主动调用// 如View有style属性时constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attributeSet, defStyleAttr, defStyleRes)}
构造方法写好后就可以进行view的绘制了,不过一般的view绘制必须要满足一些view的属性,比如warp_content的实现,在onMeasure方法中判断warp_content并默认控件宽高
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthMode = MeasureSpec.getMode(widthMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightMode = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)//设置warp_content的默认宽高val width = 400val height = 400//AT_MOST对应wrap_content;EXACTLY对应match_parentif (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(width, height)} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(width, heightSize)} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, height)}}
在这里我们的view还必须支持padding属性,否则在xml中的padding属性将失效,getWidth和getHeight都是包含了padding的,所以我们定义两个新的参数来存储去掉了padding的宽高
var mWidth = width - paddingLeft - paddingRight
var mHeight = height - paddingBottom - paddingTop
//比较并返回较小的值,由于都是正多边形,所以设定宽高相等是最合适的
mWidth = mWidth.coerceAtMost(mHeight)
mHeight = mWidth
自定义属性,在res的values目录下创建自定义资源文件attrs_my_view.xml,名字随意,按照下面的格式写后便可以在布局文件中使用这些属性
<?xml version="1.0" encoding="utf-8"?>
<resources><!--name是view类名--!><declare-styleable name="MyView"><!--自定义属性名和值的类型--!><attr name="line_color" format="color" /><attr name="line_size" format="dimension" /><attr name="floor_count" format="integer" /><attr name="angle_count" format="integer" /><attr name="text_color" format="color" /><attr name="area_color" format="color" /><attr name="text_size" format="dimension" /><attr name="default_text_padding" format="dimension" /></declare-styleable>
</resources>
绘制多边形
由于各多边形的绘制坐标有出入,所以我这里依次从三角形到正八边形都绘制了一遍,在onDraw中根据代码或xml中设置的属性个数判断绘制哪一个,这里我演示一下三角形的绘制
//首先是画笔的初始化private var paint = Paint()paint.color = lineColorpaint.strokeWidth = paintSizepaint.style = Paint.Style.STROKE//defaultTextPadding 是文本与边框的距离//textSize是文字大小(单位是px,相当于每个字的宽高)//textLength是文字最大长度,考虑美观建议标题文字长度相同//三角形宽度,边长,去掉了padding属性的参数表示整个图包括文本的宽高//这里的ww就是不包含文本的图形宽度val ww = mWidth - defaultTextPadding * 2 - textLength * textSize//中心点各对角线的夹角 一定是平分360度的val angle = (Math.PI * 2 / angleCount).toFloat()//本View有大量的三角函数使用,一些角度计算关系就不介绍了,我写的时候脑袋都疼!//三角形高度,顶点到底边的垂直线 通过三角函数计算var hh = ww * sin(angle / 2)val pathLine = Path()//中心点到各角的距离val r = ww / 2 / sin(angle / 2)
找到图形中心点!!!!这里是重要的,所有图都需要根据中心点向外绘制
/*** 图形中心点* 各正多边形的中心点横坐标X都可以是整个控件的横向中心位置* 但是由于三角形,正五边形,正七边形比较特殊* 所以它们的中心点纵坐标Y并不在整个控件的中心位置* 这个时候就需要根据实际情况做一下小运算* 比如说三角形,首先算得三角形的宽度作为底边长* 因为中心点到各角的连线夹角都是相等的* 所以根据角度和边长就可算得中心点到各夹角的连线长度* 然后根据这个长度就可已算出纵坐标Y的位置* 之后的图形绘制都需要根据中心点和中心点到各夹角的长度来计算*/val centerX = width / 2//图形中心点yval centerY = r + paddingTop + textSize + defaultTextPadding
绘制边框
//绘制边框//这里有个判断主要是满足有的需求(不想绘制边框的,应该没人会这么干吧)if (isShowLine) {for (i in 1..floorCount) {val ur = r * i / floorCount//moveTo是移动到某个点,用来作为绘制的初始点pathLine.moveTo(centerX + 0f, centerY - ur)//lineTo是从上一个点画线到当前点pathLine.lineTo(centerX + ur * sin(angle / 2), centerY + ur * cos(angle / 2))pathLine.lineTo(centerX - ur * sin(angle / 2), centerY + ur * cos(angle / 2))//close是闭合整个路径,第一个点到最后一个点连线pathLine.close()}canvas.drawPath(pathLine, paint)}
绘制连线,为了更加美观
//绘制连接线
if (isShowConnect) {pathLine.reset()pathLine.moveTo(centerX.toFloat(), centerY)pathLine.lineTo(centerX.toFloat(), centerY - r)pathLine.moveTo(centerX.toFloat(), centerY)pathLine.lineTo(centerX + r * sin(angle / 2), centerY + r * cos(angle / 2))pathLine.moveTo(centerX.toFloat(), centerY)pathLine.lineTo(centerX - r * sin(angle / 2), centerY + r * cos(angle / 2))pathLine.close()canvas.drawPath(pathLine, paint)
}
绘制属性文本
//绘制文本
paint.color = textColor
paint.textSize = textSize
paint.style = Paint.Style.FILL
canvas.drawText(title[0], centerX - textSize, centerY - r - defaultTextPadding, paint)
canvas.drawText(title[1], centerX + ww / 2 - textSize, centerY + r * cos(angle / 2) + textSize + defaultTextPadding, paint)
canvas.drawText(title[2], centerX - ww / 2 - textSize, centerY + r * cos(angle / 2) + textSize + defaultTextPadding, paint)
绘制比例覆盖
//绘制覆盖分布区域图
pathLine.reset()
pathLine.moveTo(centerX.toFloat(), centerY - r * scaleList[0] / floorCount)
pathLine.lineTo(centerX + r * scaleList[1] / floorCount * sin(angle / 2), centerY + r * scaleList[1] / floorCount * cos(angle / 2)) pathLine.lineTo(centerX - r * scaleList[2] / floorCount * sin(angle / 2), centerY + r * scaleList[2] / floorCount * cos(angle / 2))
pathLine.close()
paint.color = areaColor
canvas.drawPath(pathLine, paint)
到这呢整个三角分布图就完成了,下面贴一下各个正多边形的效果图
正四边形
正五边形
正六边形
正七边形
正八边形
结尾
代码地址:https://github.com/big-guogai/PolygonDistributionView
文章参考:https://www.jianshu.com/p/e9d8420b1b9c
Android自定义View实现三角到八角的属性分布图-雷达图(蜘蛛网图)相关推荐
- Android自定义View简单实现手绘折(曲)线滚动图效果
目录 一.简介 二.实现 三.代码 四.实现效果 一.简介 通过安卓自定义View实现根据手指点击或移动轨迹绘制出折(曲)线图并循环滚动. 二.实现 获取手指点击和移动的y坐标存入数组,设定好x间隔, ...
- Android自定义View之三角,五角星,圆形,心形图片实现
转载请标明出处:http://blog.csdn.net/joker_ya/article/details/38589677 好吧,写之前扯扯.如果是大神的话,可以忽略此文档.有兴趣的话也可以看看.这 ...
- android自定义抽奖,Android自定义view制作抽奖转盘
本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplicati ...
- Android 自定义View(四)实现股票自选列表滑动效果
一.前言 Android 开发过程中自定义 View 真的是无处不在,随随便便一个 UI 效果,都会用到自定义 View.前面三篇文章已经讲过自定义 View 的一些案例效果,相关类和 API,还有事 ...
- Android自定义View —— TypedArray
在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...
- Android 自定义View —— Canvas
上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...
- android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...
转载:http://blog.csdn.net/xiabing082/article/details/48781489 1. 大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...
- Android自定义View:ViewGroup(三)
自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...
- android 自定义图形,Android自定义View之图形图像(模仿360的刷新球自定
概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...
最新文章
- xlrd.biffh.XLRDError: Excel xlsx file; not supported解决方法
- 1亿参数4万样本BERT仍听不懂人话,我们离通用NLP能还有多远?
- Linux中gcc的编译、静态库和动态库的制作
- OpenGL中投影变换矩阵的反向推导
- 深度学习与计算机视觉系列(1)_基础介绍
- 更新maven一直在更新_不更新app,就可以一直派单了?闪送政策早知道
- 代码整洁之道1-6章总结
- Java单例模式——线程安全的懒汉模式
- 手把手教你用Python高仿一个任务管理器
- 好想学python猜谜_有人可以教我猜字谜吗 好想学 怎样才可以学好猜字谜呢
- 超长干货!最全数据指标分析!
- 第二章 Android内核和驱动程序(转)
- 单字节的乘法指令设计汇编程序11*12
- Android中MotionEvent的来源和ViewRootImpl
- 一文读懂SIMD指令集 目前最全SSE/AVX介绍
- Adodb 官方介绍
- oracle 生僻字 转码,Oracle 生僻字乱码解决方案
- 中文之星掌上狂拼手机输入法 v1.0 symbian版 绿色
- iOS开发 转屏控制 (shouldAutorotate/supportedInterfaceOrientations)不起作用
- Excel函数 - 批量合并相同内容的单元格