效果圖

實(shí)現(xiàn)分析
- 畫 9 個(gè)圓
- 根據(jù)手指觸摸的坐標(biāo)更改圓的顏色
- 畫線
- 處理手指抬起的狀態(tài)
后面的文章大部分應(yīng)該都會(huì)使用 Kotlin ,隨著 JetPack 和 Compose 的廣泛使用,Kotlin 慢慢成為了趨勢(shì)。如果對(duì) Kotlin 還不了解的推薦一下郭神的 《第一行代碼》Kotlin 版,比較容易上手的 Koltin 入門書籍。
1、畫 9 個(gè)圓
private fun initDot() {
//初始化9宮格
//記錄點(diǎn)的狀態(tài),回掉密碼
//九宮格總寬度
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
//內(nèi)圓大小
inDotWidth = outDotWidth / 6
//點(diǎn)的位置
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)
}

圖畫得不是很標(biāo)準(zhǔn),放一張圖方便理解
- 考慮到橫豎屏的問(wèn)題,所以會(huì)設(shè)置兩個(gè)方向的偏移量。如果是豎屏對(duì)應(yīng) y 軸偏移量為 0,如果是橫屏對(duì)應(yīng) x 軸偏移量為0
- 九宮格在屏幕的中間,所以對(duì)應(yīng)的豎屏偏移量為 ( height - width ) / 2,橫屏為( width - height ) / 2
- 一個(gè)圓占屏幕的 1/3,所以圓的直徑為 width / 3 ,半徑為 width / 6
- 得到上面的數(shù)據(jù),依次就可以算出 9 宮格每個(gè)中心點(diǎn)的坐標(biāo),最后將大圓和小圓繪制出來(lái)
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
}
- 首先確定手指觸摸的范圍是否在圓內(nèi),如果不在圓內(nèi)則不處理,如果在圓內(nèi)就修改當(dāng)前點(diǎn)的狀態(tài)
- 當(dāng)手指在屏幕上移動(dòng)時(shí),判斷移動(dòng)的位置是否包含了點(diǎn),如果沒(méi)有包含則將點(diǎn)添加并修改狀態(tài),如果有則不處理。
3、畫線
private fun drawLine(start: Point, end: Point, canvas: Canvas, paint: Paint) {
//拿到2點(diǎn)的坐標(biāo)和坐標(biāo)差
val startX = start.centerX
val startY = start.centerY
val endX = end.centerX
val endY = end.centerY
val dx = endX - startX
val dy = endY - startY
//計(jì)算2點(diǎn)的位置
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)
}
如果從點(diǎn)中間會(huì)稍微影響九宮格的美觀,所以要減去多余的長(zhǎng)度。貼一張圖,方便理解計(jì)算規(guī)則

4、處理手指抬起
MotionEvent.ACTION_UP -> {
isTouchPoint = false
when {
selectPoint.size == 1 -> {
clearSelectPoints()
}
selectPoint.size <= 4 -> {
showSelectError()
}
else -> {
showSelectRight()
}
}
}
這里處理比較簡(jiǎn)單:當(dāng)只存在 1 個(gè)點(diǎn)時(shí)清空狀態(tài);小于 4 個(gè)點(diǎn)時(shí)顯示錯(cuò)誤;其他情況就顯示正確