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实现三角到八角的属性分布图-雷达图(蜘蛛网图)相关推荐

  1. Android自定义View简单实现手绘折(曲)线滚动图效果

    目录 一.简介 二.实现 三.代码 四.实现效果 一.简介 通过安卓自定义View实现根据手指点击或移动轨迹绘制出折(曲)线图并循环滚动. 二.实现 获取手指点击和移动的y坐标存入数组,设定好x间隔, ...

  2. Android自定义View之三角,五角星,圆形,心形图片实现

    转载请标明出处:http://blog.csdn.net/joker_ya/article/details/38589677 好吧,写之前扯扯.如果是大神的话,可以忽略此文档.有兴趣的话也可以看看.这 ...

  3. android自定义抽奖,Android自定义view制作抽奖转盘

    本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplicati ...

  4. Android 自定义View(四)实现股票自选列表滑动效果

    一.前言 Android 开发过程中自定义 View 真的是无处不在,随随便便一个 UI 效果,都会用到自定义 View.前面三篇文章已经讲过自定义 View 的一些案例效果,相关类和 API,还有事 ...

  5. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  6. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  7. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  8. Android自定义View:ViewGroup(三)

    自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...

  9. android 自定义图形,Android自定义View之图形图像(模仿360的刷新球自定

    概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...

最新文章

  1. xlrd.biffh.XLRDError: Excel xlsx file; not supported解决方法
  2. 1亿参数4万样本BERT仍听不懂人话,我们离通用NLP能还有多远?
  3. Linux中gcc的编译、静态库和动态库的制作
  4. OpenGL中投影变换矩阵的反向推导
  5. 深度学习与计算机视觉系列(1)_基础介绍
  6. 更新maven一直在更新_不更新app,就可以一直派单了?闪送政策早知道
  7. 代码整洁之道1-6章总结
  8. Java单例模式——线程安全的懒汉模式
  9. 手把手教你用Python高仿一个任务管理器
  10. 好想学python猜谜_有人可以教我猜字谜吗 好想学 怎样才可以学好猜字谜呢
  11. 超长干货!最全数据指标分析!
  12. 第二章 Android内核和驱动程序(转)
  13. 单字节的乘法指令设计汇编程序11*12
  14. Android中MotionEvent的来源和ViewRootImpl
  15. 一文读懂SIMD指令集 目前最全SSE/AVX介绍
  16. Adodb 官方介绍
  17. oracle 生僻字 转码,Oracle 生僻字乱码解决方案
  18. 中文之星掌上狂拼手机输入法 v1.0 symbian版 绿色
  19. iOS开发 转屏控制 (shouldAutorotate/supportedInterfaceOrientations)不起作用
  20. Excel函数 - 批量合并相同内容的单元格

热门文章

  1. 创建两个文本框,一个按钮。第 1 个文本框绑定任意键事件,敲击键盘任意可显示字符,在交互窗口中显示该字符;
  2. drawio(Windows)中使用中文字体(如黑体)
  3. SpringMVC的在线人数统计监听器
  4. 互联网工作 常用名词及基础知识扫盲
  5. 调制与解调(1)——初认识
  6. VC实现复制粘贴字符串
  7. linux文件夹改不了权限吗,linux修改文件和文件夹权限及所有者
  8. docker环境下orientdb的数据库导入与导出
  9. Android 编译命令 make j8 21 | tee build.log 解释
  10. 软考-嵌入式系统设计师-笔记:信息化和知识产权基础知识