效果图
实现分析
- 画 9 个圆
- 根据手指触摸的坐标更改圆的颜色
- 画线
- 处理手指抬起的状态
后面的文章大部分应该都会使用 Kotlin ,随着 JetPack 和 Compose 的广泛使用,Kotlin 慢慢成为了趋势。如果对 Kotlin 还不了解的推荐一下郭神的 《第一行代码》Kotlin 版,比较容易上手的 Koltin 入门书籍。
1、画 9 个圆
private fun initDot() {
//初始化9宫格
//记录点的状态,回掉密码
//九宫格总宽度
var width = this.width
//九宫格总高度
var height = this.height
var offSetY = 0
var offSetX = 0
//兼容横竖屏
if (height > width) {
//Y轴偏移量
offSetY = (height - width) / 2
height = width
} else {
//X轴偏移量
offSetX = (width - height) / 2
width = height
}
//单元格宽度
var pointWidth = width / 3
//外圆大小
outDotWidth = width / 12f
//内圆大小
inDotWidth = outDotWidth / 6
//点的位置
mPoints[0][0] = Point(offSetX + pointWidth / 2, offSetY + pointWidth / 2, 0)
mPoints[0][1] = Point(offSetX + pointWidth * 3 / 2, offSetY + pointWidth / 2, 1)
mPoints[0][2] = Point(offSetX + pointWidth * 5 / 2, offSetY + pointWidth / 2, 2)
mPoints[1][0] = Point(offSetX + pointWidth / 2, offSetY + pointWidth * 3 / 2, 3)
mPoints[1][1] = Point(offSetX + pointWidth * 3 / 2, offSetY + pointWidth * 3 / 2, 4)
mPoints[1][2] = Point(offSetX + pointWidth * 5 / 2, offSetY + pointWidth * 3 / 2, 5)
mPoints[2][0] = Point(offSetX + pointWidth / 2, offSetY + pointWidth * 5 / 2, 6)
mPoints[2][1] = Point(offSetX + pointWidth * 3 / 2, offSetY + pointWidth * 5 / 2, 7)
mPoints[2][2] = Point(offSetX + pointWidth * 5 / 2, offSetY + pointWidth * 5 / 2, 8)
}
图画得不是很标准,放一张图方便理解
- 考虑到横竖屏的问题,所以会设置两个方向的偏移量。如果是竖屏对应 y 轴偏移量为 0,如果是横屏对应 x 轴偏移量为0
- 九宫格在屏幕的中间,所以对应的竖屏偏移量为 ( height - width ) / 2,横屏为( width - height ) / 2
- 一个圆占屏幕的 1/3,所以圆的直径为 width / 3 ,半径为 width / 6
- 得到上面的数据,依次就可以算出 9 宫格每个中心点的坐标,最后将大圆和小圆绘制出来
2、手指触摸更改圆的颜色
override fun onTouchEvent(event: MotionEvent): Boolean {
mMoveX = event.x
mMoveY = event.y
when (event.action) {
MotionEvent.ACTION_DOWN -> {
val point = point
if (point != null) {
isTouchPoint = true
selectPoint.add(point)
point.setStatusPress()
}
}
MotionEvent.ACTION_MOVE -> {
if (isTouchPoint) {
val point = point
if (point != null) {
if (!selectPoint.contains(point)) {
selectPoint.add(point)
}
point.setStatusPress()
}
}
}
}
invalidate()
return true
}
- 首先确定手指触摸的范围是否在圆内,如果不在圆内则不处理,如果在圆内就修改当前点的状态
- 当手指在屏幕上移动时,判断移动的位置是否包含了点,如果没有包含则将点添加并修改状态,如果有则不处理。
3、画线
private fun drawLine(start: Point, end: Point, canvas: Canvas, paint: Paint) {
//拿到2点的坐标和坐标差
val startX = start.centerX
val startY = start.centerY
val endX = end.centerX
val endY = end.centerY
val dx = endX - startX
val dy = endY - startY
//计算2点的位置
val d = (sqrt((dx * dx + dy * dy).toDouble())).toFloat()
val rx = dx / d * inDotWidth
val ry = dy / d * inDotWidth
canvas.drawLine(startX + rx, startY + ry, endX - rx, endY - ry, paint)
}
如果从点中间会稍微影响九宫格的美观,所以要减去多余的长度。贴一张图,方便理解计算规则
4、处理手指抬起
MotionEvent.ACTION_UP -> {
isTouchPoint = false
when {
selectPoint.size == 1 -> {
clearSelectPoints()
}
selectPoint.size <= 4 -> {
showSelectError()
}
else -> {
showSelectRight()
}
}
}
这里处理比较简单:当只存在 1 个点时清空状态;小于 4 个点时显示错误;其他情况就显示正确