• Android~Compose之自定义View


    系列文章目录



    目标

    • 玩转 Compose 的自定义View
    • 实现一个手动划线的 DrawView
    • 实现一个进度条的 LevelView

    实现一个手动划线的 DrawView

    需求:实现一个划线的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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    pointerInteropFilter 进行触摸事件处理和原来的差不多
    Pair> 用于记录坐标,按下的为 true ,其他为false,false 连在一起

    实现一个进度条的 LevelView

    需求:实现绘制弧形,进度显示

    @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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    rotate(ration, center) {} 选择函数,如果是以往的是view.rotate(ration,center) 用括号包裹的内容是作用的对象区域,在使用时要加以区别
    其他绘制和原来的区别不大

  • 相关阅读:
    【21天学习挑战赛】顺序查找和二分查找的小总结
    vue脚手架及常用指令集合
    C++11 Forward
    【系统设计】设计一个短链接系统
    【luogu AT2382】A or...or B Problem(思维)
    ArcGIS实验教程——实验四十六:地图概括功能实验教程
    怎么从零编写一个 v3 版本的 chrome 浏览器插件实现 CSDN 博客网站的暗黑和明亮主题切换?
    什么是缓存雪崩、缓存穿透、缓存击穿?怎么解决?
    猿人学第16题js逆向-window蜜罐
    linux嵌入式开发所用工具
  • 原文地址:https://blog.csdn.net/Bluechalk/article/details/120686754