作用图

用jetpack-compose写个进度条,始于进度条,但不终于进度条

始于进展条,但不终于进展条

我也不知道为啥要起个这么傻的标题。很屡次了,每次学习自定义view后,搞了一段时间就草草收场,这东西是这样的,自身不像咱们日常运用的比方什么一些个代码写法什么的,滚瓜烂熟。这自己写view一段时间不搞,感觉就不会了一样。再一个是,公司的管理人员不会等你磨磨唧唧的写完一个没有bug的view,所以我或许说大多数人一看到不常用的view,立刻就跑去百度,google,github上翻有没有现已写好的,命运好点,一些个开源作者现已把功用封装好了,拿来即用(以上做法我认为是正确的,可是对咱们自身是完全没有提高),横竖我是很少想过为什么这么完成,这也不重要,重要的是我没有自己去着手完成一遍。说了挺多废话的,现在开始功用的完成。如同都没扯到标题中的进展条???

免责声明

由于代码测验少,没封装,经不起检测,仅供学习。还有一个是作用是我学习的某篇文章的,可是我找不到原文了,抱愧。

用jetpack-compose写个进度条,始于进度条,但不终于进度条

功用思路

自身也没啥好说的,东西太简略了,就当写给还没触摸过的同学吧,有经历的同学完全能够跳过整篇文章或许学习一下compose中怎么完成。
我说下我的思路,有些同学反响快能够一步到位。首要需求将功用分化,千里之行,始于足下。

用jetpack-compose写个进度条,始于进度条,但不终于进度条

  1. 静态的底部圆环
  2. 动态的上方圆弧:圆环的stroke略大于底部圆环
  3. 文字的制作:百分比和“出勤率”是以圆心的水平线为分界线
  4. 接着考虑再考虑百分比文字和进展的动画作用

制作内部圆环和外部圆弧

Canvas(modifier = Modifier.size(300.dp), onDraw = {
    val innerStrokeWidth = 10.dp.toPx()
    val radius = 120.dp.toPx()
    val outStrokeWidth = 17.dp.toPx()
    val canvasWidth = size.width
    val canvasHeight = size.height
    //内部圆
    drawCircle(
        Color(222, 228, 246),
        radius = radius,
        center = Offset(canvasWidth / 2, canvasHeight / 2),
        style = Stroke(innerStrokeWidth)
    )
    //圆弧进展
    drawArc(
        Color(46, 120, 249),
        startAngle = -90f,
        sweepAngle = 120f,
        useCenter = false,
        size = Size(radius * 2, radius * 2),
        style = Stroke(outStrokeWidth, cap = StrokeCap.Round),
        topLeft = Offset(center.x - radius, center.y - radius)
    )
})

定义的变量从姓名应该比较简单看出来,需求留意的是.toPx()和.toDp()等这种便捷办法只有在DrawScope区域里也便是onDraw = { 区域 } 里才能运用。 drawCircle中Offset用于确认圆的圆心,size也是也是DrawScope区域内的特点,可获取Canvas尺度等信息,比方size.width获取的便是设置画布的宽度为300dp(转成像素)。
还一个重点是drawArc中的size和topLeft特点。咱们需求画的是圆弧,size用于确认制作的范围,所以天然就能够确认size是一个正方形。
却是这个topLeft我当时想了挺久到底是个啥,其实便是字面意思,让你指定:区域的左上角在哪里

用jetpack-compose写个进度条,始于进度条,但不终于进度条

有人能引荐下免费的好用的画图东西吗,自带的用着不舒服啊。

制作文字

目前drawText有四种办法,主要是两种,如下图12行为榜首种办法,34行为第二种办法

用jetpack-compose写个进度条,始于进度条,但不终于进度条

榜首种和第二种区别不大,主要是色彩是传递Brush仍是Color。
对着下方的一张图(drawText办法特点)和一份代码看:假如是运用的第二种办法,需求传递的榜首个参数为TextMeasurer,也便是val textMeasure = rememberTextMeasurer(),没有后边的measure(……)。运用这种办法有个当地需求留意,假如你需求设置文本的style特点,并且特点会导致文本的大小呈现改动,比方加粗,改动文本的sp,这个当地有个坑便是你在drawText()的时分,里边有个参数需求你设置style,当你设置了改动文字的style后,你认为没任何问题,成果你会发现写出来的UI和你的期望有误差,这儿需求别的调用textMeasure.measure(….),里边再设置一遍你的style,比较的费事。所以我仍是引荐我下面这种写法,假如没理解我在说什么,能够自己在编辑器里对着drawText特点看一下,光看不做假把式。

用jetpack-compose写个进度条,始于进度条,但不终于进度条


val textPercent = "60%"
//丈量文字
val textPercentLayResult = rememberTextMeasurer().measure(
    text = AnnotatedString(textPercent),
    style = TextStyle(
        color = Color(96, 98, 172),
        fontSize = 30.sp,
        fontWeight = FontWeight.Bold
    )
)
Canvas(modifier = Modifier.size(300.dp), onDraw = {
    val canvasWidth = size.width
    val canvasHeight = size.height
    val textPercentWidth = textPercentLayResult.size.width
    val textPercentHeight = textPercentLayResult.size.height
    //百分比文字
    drawText(
        textLayoutResult = textPercentLayResult,
        topLeft = Offset(
            canvasWidth / 2 - textPercentWidth / 2,
            canvasHeight / 2 - textPercentHeight
        ),
    )
})

topLeft的特点现已很明显了,刚才讲圆弧的时分讲过了,便是榜首个文字的左上方,其他的变量名也比较简单知道用途

动画作用

在compose里的动画作用(不止动画)比较原生真的是简单太多了

//sweepState便是外部传进来的百分比,比方最大是100,
当时是60,那么sweepState便是0.6,目标值便是360*0.6=60,  
所以扫过的视点animAngle便是60度。
val animAngle = animateFloatAsState(
    targetValue = sweepState.value * 360,
    animationSpec = tween(1000)
)
//0.6 * 100 = 60便是咱们显现的百分比
val animPercent = animateIntAsState(
    targetValue = (sweepState.value * 100).toInt(),
    animationSpec = tween(1000)
)
//赋值给textPercent,传递到要丈量的文本中
val textPercent = "${animPercent.value}%"
val textPercentLayResult = rememberTextMeasurer().measure(
    text = AnnotatedString(textPercent),
    。。。
)
drawArc(
    。。。
    sweepAngle = animAngle.value,
    。。。
)

animateFloatAsState的第二个参数是装备动画的特点,有爱好能够看下他人的文章了解下有哪些动画装备特点,横竖我是记不清。 出勤率的制作没啥好说,就单纯是个文本比较简略,同“百分比”文字

源码

@Composable
fun Progress() {
    val sweepState = remember {
        mutableStateOf(0f)
    }
    val max = 100f
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Spacer(modifier = Modifier.height(20.dp))
        ProgressBarView(sweepState)
        Spacer(modifier = Modifier.height(20.dp))
        Button(onClick = {
            sweepState.value = Random.nextInt(1 until 99) / max
        }) {
            Text(text = "按钮")
        }
    }
}
@OptIn(ExperimentalTextApi::class)
@Composable
private fun ProgressBarView(sweepState: MutableState<Float>) {
    val animAngle = animateFloatAsState(
        targetValue = sweepState.value * 360,
        animationSpec = tween(1000)
    )
    val animPercent = animateIntAsState(
        targetValue = (sweepState.value * 100).toInt(),
        animationSpec = tween(1000)
    )
    val textPercent = "${animPercent.value}%"
    val textPercentLayResult = rememberTextMeasurer().measure(
        text = AnnotatedString(textPercent),
        style = TextStyle(
            color = Color(96, 98, 172),
            fontSize = 30.sp,
            fontWeight = FontWeight.Bold
        )
    )
    val textDesc = "出勤率"
    val textDescLayoutResult = rememberTextMeasurer().measure(
        AnnotatedString(textDesc),
        TextStyle(color = Color(178, 193, 209))
    )
    Canvas(modifier = Modifier.size(300.dp), onDraw = {
        val innerStrokeWidth = 10.dp.toPx()
        val radius = 120.dp.toPx()
        val outStrokeWidth = 17.dp.toPx()
        val canvasWidth = size.width
        val canvasHeight = size.height
        //内部圆
        drawCircle(
            Color(222, 228, 246),
            radius = radius,
            center = Offset(canvasWidth / 2, canvasHeight / 2),
            style = Stroke(innerStrokeWidth)
        )
        //圆弧进展
        drawArc(
            Color(46, 120, 249),
            startAngle = -90f,
            sweepAngle = animAngle.value,
            useCenter = false,
            size = Size(radius * 2, radius * 2),
            style = Stroke(outStrokeWidth, cap = StrokeCap.Round),
            topLeft = Offset(center.x - radius, center.y - radius)
        )
        val textPercentWidth = textPercentLayResult.size.width
        val textPercentHeight = textPercentLayResult.size.height
        //百分比文字
        drawText(
            textLayoutResult = textPercentLayResult,
            topLeft = Offset(
                canvasWidth / 2 - textPercentWidth / 2,
                canvasHeight / 2 - textPercentHeight
            ),
        )
        val textDescWidth = textDescLayoutResult.size.width
        val textDescHeight = textDescLayoutResult.size.height //用不着
        //出勤率
        drawText(
            textLayoutResult = textDescLayoutResult,
            topLeft = Offset(
                canvasWidth / 2 - textDescWidth / 2,
                canvasHeight / 2
            ),
        )
    })
}

最后

如同看起来完成的代码量好多一样,其实假如不换行的话真没几行,比较原生的完成我只想说:咱们compose太厉害啦。下课

学到东西的同学点点重视,学习进步不走失