Android 使用Kotlin来实现自定义View之雷达图
Posted Adan0520
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 使用Kotlin来实现自定义View之雷达图相关的知识,希望对你有一定的参考价值。
本篇文章讲的是Kotlin 自定义view之实现雷达图。
按照惯例,我们先来看看效果图
一、先总结下自定义View的步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
3、重写onMesure
4、重写onDraw
其中onMesure方法不一定要重写,但大部分情况下还是需要重写的
二、View 的几个构造函数:
1、constructor(mContext: Context)
—>java代码直接new一个RulerView实例的时候,会调用这个只有一个参数的构造函数;
2、constructor(mContext: Context, attrs: AttributeSet)
—>在默认的XML布局文件中创建的时候调用这个有两个参数的构造函数。AttributeSet类型的参数负责把XML布局文件中所自定义的属性通过AttributeSet带入到View内;
3、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int)
—>构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或者Activity所用的Theme中的默认Style,且只有在明确调用的时候才会调用
4、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int,defStyleRes:Int)
—>该构造函数是在API21的时候才添加上的
三、下面我们就开始来看看代码啦:
1、自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的需要用到的属性以及声明相对应属性的取值类型
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RadarView">
<!-- 绘制文本的颜色 -->
<attr name="color_text" format="color" />
<!-- 绘制正多边形区域的颜色 -->
<attr name="color_main_region" format="color" />
<!-- 绘制正多边形边框或者圆圈的颜色 -->
<attr name="color_main_frame" format="color" />
<!-- 绘制数据区域1的颜色 -->
<attr name="color_region1" format="color" />
<!-- 绘制数据区域1实际边框的颜色 -->
<attr name="color_actual_frame1" format="color" />
<!-- 绘制数据区域2的颜色 -->
<attr name="color_region2" format="color" />
<!-- 绘制数据区域2实际边框的颜色 -->
<attr name="color_actual_frame2" format="color" />
<!-- 绘制数据区域3的颜色 -->
<attr name="color_region3" format="color" />
<!-- 绘制数据区域3实际边框的颜色 -->
<attr name="color_actual_frame3" format="color" />
<!-- 绘制数据区域1要不要使用渐变色 -->
<attr name="region1_gradient_enable" format="boolean" />
<!-- 绘制数据区域1渐变的开始颜色 -->
<attr name="color_region1_start" format="color" />
<!-- 绘制数据区域1渐变的结束颜色 -->
<attr name="color_region1_end" format="color" />
<!-- 要不要绘制正多边形区域 -->
<attr name="main_region_enable" format="boolean" />
<!-- 要不要绘制数据区域1 -->
<attr name="region1_enable" format="boolean" />
<!-- 要不要绘制数据区域1实际边框 -->
<attr name="ractual_frame1_enable" format="boolean" />
<!-- 要不要绘制数据区域2 -->
<attr name="region2_enable" format="boolean" />
<!-- 要不要绘制数据区域2实际边框 -->
<attr name="ractual_frame2_enable" format="boolean" />
<!-- 要不要绘制数据区域3 -->
<attr name="region3_enable" format="boolean" />
<!-- 要不要绘制数据区域3实际边框 -->
<attr name="ractual_frame3_enable" format="boolean" />
<!--文字的大小-->
<attr name="textSize" format="dimension" />
</declare-styleable>
</resources>
2、在布局文件中引用,一定要引入xmlns:app=”http://schemas.android.com/apk/res-auto”,Android Studio中我们可以使用res-atuo命名空间,就不用在添加自定义View全类名。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#18191D">
<co.per.radarview.RadarView
android:id="@+id/view_radar1"
android:layout_width="210dp"
android:layout_height="210dp"
app:color_actual_frame1="@color/Actualframe1"
app:color_actual_frame2="@color/ActualFrame2"
app:color_actual_frame3="@color/ActualFrame3"
app:color_main_frame="@color/MainFrame"
app:color_main_region="@color/MainRegion"
app:color_region1="@color/Region1"
app:color_region2="@color/Region2"
app:color_region3="@color/Region3"
app:color_text="@color/Text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:ractual_frame3_enable="true"
app:region3_enable="true" />
<co.per.radarview.RadarView
android:id="@+id/view_radar2"
android:layout_width="210dp"
android:layout_height="210dp"
app:color_actual_frame1="@color/Actualframe1"
app:color_actual_frame2="@color/ActualFrame2"
app:color_actual_frame3="@color/ActualFrame3"
app:color_main_frame="@color/MainFrame"
app:color_main_region="@color/MainRegion"
app:color_region1="@color/Region1"
app:color_region2="@color/Region2"
app:color_region3="@color/Region3"
app:color_text="@color/Text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view_radar1"
app:ractual_frame3_enable="true"
app:region3_enable="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
3、在View的构造方法中,获得我们的自定义的样式,并实现雷达图
package co.per.radarview
import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin
/**
* 雷达图
* 六维图
* 三维图
* Created by juan on 2021/07/20.
*/
class RadarView : View {
/**
* 字体颜色
*/
private var mTextColor = 0
/**
* 绘制正多边形区域的颜色
*/
private var mMainRegionColor = 0
/**
* 绘制正多边形边框或者圆圈的颜色
*/
private var mMainFrameColor = 0
/**
* 绘制数据区域1的颜色
*/
private var mRegion1Color = 0
/**
* 绘制数据区域1实际边框的颜色
*/
private var mActualframe1Color = 0
/**
* 绘制数据区域1要不要使用渐变色
*/
private var mRegion1GradientEnable = false
/**
* 绘制数据区域1渐变的开始颜色
*/
private var mRegion1StartColor = 0
/**
* 绘制数据区域1渐变的结束颜色
*/
private var mRegion1EndColor = 0
/**
* 要不要绘制数据区域1
*/
private var mRegion1Enable = true
/**
* 要不要绘制数据区域1实际边框
*/
private var mActualframe1Enable = true
/**
* 绘制数据区域2的颜色
*/
private var mRegion2Color = 0
/**
* 绘制数据区域2实际边框的颜色
*/
private var mActualFrame2Color = 0
/**
* 要不要绘制数据区域2
*/
private var mRegion2Enable = true
/**
* 要不要绘制数据区域2实际边框
*/
private var mActualframe2Enable = true
/**
* 绘制数据区域3的颜色
*/
private var mRegion3Color = 0
/**
* 绘制数据区域3实际边框的颜色
*/
private var mActualFrame3Color = 0
/**
* 要不要绘制数据区域3
*/
private var mRegion3Enable = false
/**
* 要不要绘制数据区域3实际边框
*/
private var mActualframe3Enable = false
/**
* 要不要绘制正多边形区域
*/
private var mMainRegionEnable = false
/**
* 用于创建线性渐变效果
*/
private var gradient: LinearGradient? = null
/**
* 中心X
*/
private var centerX = 0
/**
* 中心Y
*/
private var centerY = 0
/**
* 网格最大半径
*/
private var radius = 0f
/**
* 绘制正多边形边框或者圆圈的画笔
*/
private var mainFramePaint: Paint? = null
/**
* 绘制正多边形区域的画笔
*/
private var mainRegionPaint: Paint? = null
/**
* 文本的画笔
*/
private var textPaint: Paint? = null
/**
* 数据区域1的画笔
*/
private var regionPaint1: Paint? = null
/**
* 数据区域1实际边框的画笔
*/
private var actualFramePaint1: Paint? = null
/**
* 数据区域2的画笔
*/
private var regionPaint2: Paint? = null
/**
* 数据区域2实际边框的画笔
*/
private var actualFramePaint2: Paint? = null
/**
* 数据区域3的画笔
*/
private var regionPaint3: Paint? = null
/**
* 数据区域3实际边框的画笔
*/
private var actualFramePaint3: Paint? = null
/**
* 文字大小
*/
private var fontSize = 32f
/**
* 绘制几边形
*/
private var count = 6
private var angle = Math.PI / count * 2
private var titles = arrayOf("轻量", "美观", "抓地", "耐磨", "缓震", "透气")
private var data1 = doubleArrayOf(100.0, 65.0, 45.0, 70.0, 80.0, 100.0, 90.0, 80.0) //各维度分值
private var data2 = doubleArrayOf(80.0, 90.0, 95.0, 80.0, 95.0, 40.0, 90.0, 100.0) //各维度分值
private var data3 = doubleArrayOf(90.0, 80.0, 65.0, 100.0, 65.0, 70.0, 50.0, 60.0) //各维度分值
private val maxValue = 100f //数据最大值
//多边形每一条先的起始坐标和终点坐标
private var endX = 0f
private var endY = 0f
constructor(context: Context?) : this(context, null)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initView(attrs)
initPaint()
}
/**
* 获取自定义属性
*/
private fun initView(attrs: AttributeSet?) {
val t = context.obtainStyledAttributes(attrs, R.styleable.RadarView)
this.mTextColor = t.getColor(R.styleable.RadarView_color_text,mTextColor)
this.mMainRegionColor = t.getColor(R.styleable.RadarView_color_main_region,mMainRegionColor)
this.mMainFrameColor = t.getColor(R.styleable.RadarView_color_main_frame,mMainFrameColor)
this.mRegion1Color = t.getColor(R.styleable.RadarView_color_region1,mRegion1Color)
this.mActualframe1Color = t.getColor(R.styleable.RadarView_color_actual_frame1,mActualframe1Color)
this.mRegion2Color = t.getColor(R.styleable.RadarView_color_region2,mRegion2Color)
this.mActualFrame2Color = t.getColor(R.styleable.RadarView_color_actual_frame2,mActualFrame2Color)
this.mRegion3Color = t.getColor(R.styleable.RadarView_color_region3,mRegion3Color)
this.mActualFrame3Color = t.getColor(R.styleable.RadarView_color_actual_frame3,mActualFrame3Color)
this.mRegion1GradientEnable = t.getBoolean(R.styleable.RadarView_region1_gradient_enable,false)
this.mRegion1StartColor = t.getColor(R.styleable.RadarView_color_region1_start,mRegion1StartColor)
this.mRegion1EndColor = t.getColor(R.styleable.RadarView_color_region1_end,mRegion1EndColor)
this.mMainRegionEnable = t.getBoolean(R.styleable.RadarView_main_region_enable,false)
this.mRegion1Enable = t.getBoolean(R.styleable.RadarView_region1_enable,true)
this.mActualframe1Enable = t.getBoolean(R.styleable.RadarView_ractual_frame1_enable,true)
this.mRegion2Enable = t.getBoolean(R.styleable.RadarView_region2_enable,true)
this.mActualframe2Enable = t.getBoolean(R.styleable.RadarView_ractual_frame2_enable,true)
this.mRegion3Enable = t.getBoolean(R.styleable.RadarView_region3_enable,false)
this.mActualframe3Enable = t.getBoolean(R.styleable.RadarView_ractual_frame3_enable,false)
this.fontSize = t.getDimension(R.styleable.RadarView_textSize, dpToPx(14f).toFloat())
}
/**
* 初始化画笔
*/
private fun initPaint() {
//绘制正多边形边框或者圆圈的画笔
mainFramePaint = Paint()
mainFramePaint!!.color = mMainFrameColor
mainFramePaint!!.isAntiAlias = true
mainFramePaint!!.strokeWidth = 4f
mainFramePaint!!.style = Paint.Style.STROKE
//绘制正多边形区域的画笔
mainRegionPaint = Paint()
mainRegionPaint!!.style = Paint.Style.FILL
mainRegionPaint!!.color = mMainRegionColor
//文本的画笔
textPaint = Paint()
textPaint!!.color = mTextColor
textPaint!!.isAntiAlias = true
textPaint!!.textSize = fontSize
textPaint!!.style = Paint.Style.FILL
//数据区域1的画笔
regionPaint1 = Paint()
regionPaint1!!.style = Paint.Style.FILL_AND_STROKE
regionPaint1!!.color = mRegion1Color
regionPaint1!!.isAntiAlias = true
//数据区域1实际边框的画笔
actualFramePaint1 = Paint()
actualFramePaint1!!.color = mActualframe1Color
actualFramePaint1!!.isAntiAlias = true
actualFramePaint1!!.strokeWidth = 0f
actualFramePaint1!!.style = Paint.Style.STROKE
//数据区域2的画笔
regionPaint2 = Paint()
regionPaint2!!.style = Paint.Style.FILL_AND_STROKE
regionPaint2!!.color = mRegion2Color
regionPaint2!!.isAntiAlias = true
//数据区域2实际边框的画笔
actualFramePaint2 = Paint()
actualFramePaint2!!.color = mActualFrame2Color
actualFramePaint2!!.isAntiAlias = true
actualFramePaint2!!.strokeWidth = 0f
actualFramePaint2!!.style = Paint.Style.STROKE
//数据区域3的画笔
regionPaint3 = Paint()
regionPaint3!!.style = Paint.Style.FILL_AND_STROKE
regionPaint3!!.color = mRegion3Color
regionPaint3!!.isAntiAlias = true
//数据区域3实际边框的画笔
actualFramePaint3 = Paint()
actualFramePaint3!!.color = mActualFrame3Color
actualFramePaint3!!.isAntiAlias = true
actualFramePaint3!!.strokeWidth = 0f
actualFramePaint3!!.style = Paint.Style.STROKE
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
centerX = w / 2
centerY = h / 2
radius = h.coerceAtMost(w) / 2 - textPaint!!.measureText(titles[0]) //半径为宽高的一半减去文字的长度
invalidate() // 工作在ui线程
super.onSizeChanged(w, h, oldw, oldh)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (mMainRegionEnable){
drawPolygon(canvas, radius)
}
if (count < 4){
drawmCircle(canvas)
}else{
drawPolygon(canvas)
}
drawLines(canvas)
drawText(canvas)
if (mRegion3Enable){
drawRegion3(canvas)
}
if (mActualframe3Enable){
drawFrame3(canvas)
}
if (mRegion2Enable){
drawRegion2(canvas)
}
if (mActualframe2Enable){
drawFrame2(canvas)
}
if (mRegion1Enable){
drawRegion1(canvas)
}
if (mActualframe1Enable){
drawFrame1(canvas)
}
}
/**
* 绘制正多边形区域
*/
private fun drawPolygon(canvas: Canvas, radius: Float) {
val path = Path()
path.moveTo(centerX.toFloat(), centerY - radius)
for (i in 0 until count) {
endX = (centerX + radius * sin(angle * i)).toFloatAndroid 使用Kotlin来实现自定义View之雷达图
Android 使用Kotlin来实现自定义View之雷达图
Android 使用Kotlin来实现水波纹的自定义View
Android 使用Kotlin来实现水波纹的自定义View