一、轨迹的画法
轨迹的难点主要在于位置的测量,比如我们想得到下方的仪表盘(黑色部分):
预设常量
val DASH_WIDTH = 2f.dp2px //刻度的宽
val DASH_LENGTH = 10f.dp2px //刻度的长
val RAIUS = 100f.dp2px //圆弧的半径
val LENGTH = 80f.dp2px //指针的长度
const val OPEN_ANGLE = 120f //圆弧开口的角
dp2px为Float的扩展属性
/**
* Float的扩展属性
*/
val Float.dp2px
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
Resources.getSystem().displayMetrics
)
1、画圆弧
val paint = Paint(Paint.ANTI_ALIAS_FLAG) //圆弧的画笔
val arcPath = Path() //圆弧的路径
paint.strokeWidth = 2f //画笔的宽度
paint.style = Paint.Style.STROKE //画笔的模式
//设置路径
arcPath.addArc(
width / 2f - RAIUS,
height / 2f - RAIUS,
width / 2f + RAIUS,
height / 2f + RAIUS,
90 + OPEN_ANGLE / 2f,
360 - OPEN_ANGLE
)
//在onDraw方法中画圆弧
canvas.drawPath(arcPath, paint)
效果展示
画圆弧主要点在于Path路径的设置,通过设置左上右下4个点来定义一个矩形,然后在矩形内画弧。
2、画刻度
画刻度的思路为画虚线,设定好虚线实心段的长宽和间距即可。画虚线的路径与圆弧的路径相同。
设置一根画笔
private val paintEffect = Paint(Paint.ANTI_ALIAS_FLAG) //画刻度
paintEffect.strokeWidth = 2f //画笔的二条属性
paintEffect.style = Paint.Style.STROKE
设置二个Path,其中一个是虚线实心段的Path,一个是整个虚线的Path。
private val dashPath = Path() //虚线实心段的Path
private val arcPath = Path() //虚线的Path
//虚线实心段的Path
dashPath.addRect(0f, 0f, DASH_WIDTH, DASH_LENGTH, Path.Direction.CCW)
//虚线的Path
arcPath.addArc(
width / 2f - RAIUS,
height / 2f - RAIUS,
width / 2f + RAIUS,
height / 2f + RAIUS,
90 + OPEN_ANGLE / 2f,
360 - OPEN_ANGLE)
这里就会有一个疑问,虚线的间隔应该如何计算,也就是刻度的间隔应该如何计算?
1)计算虚线的Path的长度
//测量Path的长度
val arcPathMessure = PathMeasure(arcPath, false) //false path不封口
2)计算虚线的间距
(arcPathMessure.length - DASH_WIDTH) / 20f //这里要减掉一个虚线(刻度)的宽度
3)将虚线的属性设置给画笔
paintEffect.pathEffect =
PathDashPathEffect(
dashPath,
(arcPathMessure.length - DASH_WIDTH) / 20f,
0f,
PathDashPathEffect.Style.ROTATE
)
最后,我们完成画刻度的工作
canvas.drawPath(arcPath, paintEffect)
效果展示
3、画指针
指针的画法需要对三角函数有个基本的概念
正弦:sin α = y/r
余弦:cos α = x/r
正切:tan α = y/x
正割:sec α = 1/cosα = r/x
余割:csc α = 1/sinα = r/y
余切:cot α = 1/tanα = x/y
计算指针的角度,比如第五个刻度(开头的刻度不算)
90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * 5
转化为代码
val radians = Math.toRadians((90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * 5).toDouble())
画指针其实是画一条线,指定XY轴上的起点和终点
//画指针
canvas.drawLine(
width / 2f,
height / 2f,
width / 2f + cos(radians).toFloat() * LENGTH,
height / 2f + sin(radians).toFloat() * LENGTH,
paint
)
效果展示
将刻度的代码抽出来
/**
* 画指针
* @param pointer 指针上的刻度
*/
private fun drawPointer(canvas: Canvas, pointer: Int) {
//角度
val radians =
Math.toRadians((90 + OPEN_ANGLE / 2f + (360 - OPEN_ANGLE) / 20f * pointer).toDouble())
//画指针
canvas.drawLine(
width / 2f,
height / 2f,
width / 2f + cos(radians).toFloat() * LENGTH,
height / 2f + sin(radians).toFloat() * LENGTH,
paint
)
}
4、知识点总结
1)注意实线与虚线的画法,PathDashPathEffect的使用
2)注意Path的测量,PathMessure的使用
3)注意虚线间距的计算,需要减掉一个实心虚线的宽度
4)注意三角函数的使用
二、饼图的画法
预设常量
private val PIE_RAIUS = 100f.dp2px //饼图的半径
private val ANGLES = floatArrayOf(60f, 90f, 120f, 90f) //饼图的角度
private val COLORS = listOf<Int>( //饼图的颜色
Color.parseColor("#C2185B"),
Color.parseColor("#00ACC2"),
Color.parseColor("#558B2D"),
Color.parseColor("#5D4024")
)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG) //画饼的画笔
1、画饼图
/**
* 画饼图
*/
private fun drawPie(canvas: Canvas) {
var tempAngle = 0f
for ((index, angle) in ANGLES.withIndex()) {
paint.color = COLORS[index]
canvas.drawArc(
width / 2f - PIE_RAIUS,
height / 2f - PIE_RAIUS,
width / 2f + PIE_RAIUS,
height / 2f + PIE_RAIUS,
tempAngle, //起始角
angle, //偏移量(这里容易写错,写错成tempAngle+angle)
true, //封口
paint
)
tempAngle += angle
}
}
2、画带偏移量的饼图
/**
* 画饼图具有偏移量
* @param translateValue 偏移的量
* @param num 第几个偏移
*/
private fun drawPieWithTranslate(canvas: Canvas, translateValue: Int,num:Int) {
var tempAngle = 0f
for ((index, angle) in ANGLES.withIndex()) {
paint.color = COLORS[index]
if (index == num) { //哪个偏移
canvas.save()
var radius = Math.toRadians((tempAngle + angle / 2).toDouble()) //计算角度
canvas.translate( //偏移的api,同样涉及三角函数的计算
cos(radius).toFloat() * translateValue,
sin(radius).toFloat() * translateValue
)
}
canvas.drawArc(
width / 2f - PIE_RAIUS,
height / 2f - PIE_RAIUS,
width / 2f + PIE_RAIUS,
height / 2f + PIE_RAIUS,
tempAngle,
angle,
true,
paint
)
tempAngle += angle
if (index == num) { //哪个恢复
canvas.restore()
}
}
}
效果图
3、知识点总结
1)偏移的Api是canvas.translate
2)注意drawArc方法角度的设置
3)注意canvas.save和canvas.restore的使用
--个人学习笔记--