需求:实现一个划线的View,支持撤销,手指按下后,进行滑动,画出线段,抬起手结束。
@SuppressLint("UnrememberedMutableState")
@Preview("drawView2")
@Composable
fun DrawView2() {
var pathSave: SnapshotStateList<Pair<Boolean, Pair<Float, Float>>?> = mutableStateListOf(null)
Box(modifier = Modifier.fillMaxSize()) {
Canvas(
modifier = Modifier
.fillMaxSize()
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> {
pathSave.add(Pair(true, Pair(it.x, it.y)))
}
MotionEvent.ACTION_MOVE -> {
pathSave.add(Pair(false, Pair(it.x, it.y)))
}
MotionEvent.ACTION_UP -> {
pathSave.add(Pair(false, Pair(it.x, it.y)))
}
else -> false
}
true
}
) {
drawPath(
path = pathSave.toPath(),
color = Color.Yellow,
alpha = 1f,
style = Stroke(4.dp.toPx())
)
}
Text(
modifier = Modifier
.padding(20.dp)
.background(Color(0xFF00ffff), RoundedCornerShape(12.dp))
.padding(10.dp)
.clickable {
pathSave.retract()
},
text = "回撤"
)
}
}
private fun SnapshotStateList<Pair<Boolean, Pair<Float, Float>>?>.retract(): SnapshotStateList<Pair<Boolean, Pair<Float, Float>>?> {
var lastStart = 0;
forEachIndexed { index, item ->
if (item != null && item.first) {
lastStart = index
}
}
if (lastStart != 0) {
removeRange(lastStart, size)
removeLast()
} else {
clear()
}
return this
}
fun SnapshotStateList<Pair<Boolean, Pair<Float, Float>>?>.toPath(): Path {
val path = Path()
forEach {
if (it != null) {
if (it.first) {
path.moveTo(it.second.first, it.second.second)
} else {
path.lineTo(it.second.first, it.second.second)
}
}
}
return path
}
pointerInteropFilter 进行触摸事件处理和原来的差不多
Pair
需求:实现绘制弧形,进度显示
@Preview("levelView")
@Composable
fun LevelView(singleAngle: Float = 30F, levelSize: Int = 8, angleValue: Float = 45F) {
val size = 300.dp
val lineSize = 20.dp
//计算出旋转的角度
val ration = ration(levelSize * singleAngle)
val slipSize = 360 / singleAngle / 2
val slipWidth = 5.dp
Canvas(
modifier = Modifier
.size(size)
) {
drawIntoCanvas { canvas ->
val lineWidth = lineSize.toPx()
rotate(ration, center) {
canvas.saveLayer(
Rect(
Offset.Zero,
Size(size.toPx(), size.toPx())
),
Paint()
)
drawArc(
color = Color.Yellow,
startAngle = 0F, sweepAngle = levelSize * singleAngle,
useCenter = false,
topLeft = Offset(lineWidth / 2, lineWidth / 2),
size = Size(
size.toPx() - lineWidth,
size.toPx() - lineWidth,
),
alpha = 1.0f,
style = Stroke(lineWidth),
)
drawArc(
color = Color.Red,
startAngle = 0F, sweepAngle = angleValue,
useCenter = false,
topLeft = Offset(lineWidth / 2, lineWidth / 2),
size = Size(
size.toPx() - lineWidth,
size.toPx() - lineWidth,
),
alpha = 1.0f,
style = Stroke(lineWidth),
)
//划线
for (index in 0..slipSize.toInt()) {
rotate(index * singleAngle, center) {
drawLine(
Color.Yellow,
Offset(0f, size.toPx() / 2),
Offset(size.toPx(), size.toPx() / 2),
slipWidth.toPx(),
blendMode = BlendMode.DstOut
)
}
}
canvas.restore()
}
}
}
}
fun ration(maxAngle: Float): Float {
return if (maxAngle <= 90f) {
270F - (maxAngle / 2)
} else if (maxAngle > 90F && maxAngle <= 180F) {
180F - (maxAngle / 2)
} else if (maxAngle > 180F && maxAngle <= 270F) {
180F - (maxAngle + 180 - 360) / 2
} else {
90F + (450F - (maxAngle + 90)) / 2
}
}
rotate(ration, center) {} 选择函数,如果是以往的是view.rotate(ration,center) 用括号包裹的内容是作用的对象区域,在使用时要加以区别
其他绘制和原来的区别不大