Compose 简单自定义SeekBar

357 阅读1分钟

Compose 简单自定义SeekBar

背景

抱着学习的心态,想了解一下Compose如何实现自定义View。刚好最近需要实现一个简单的进度条用Compose实现一下。

屏幕截图 2024-04-25 175053.png

屏幕截图 2024-04-25 180400.png

实现

@Composable
fun SeekBar(
    leftDrawable: Int? = R.drawable.ic_volume,
    progressFirst: Int = 50,  
    padding: Dp = 5.dp,  
    height: Dp = 42.dp,     
    width: Dp = 320.dp,
    textSize: Dp = 14.dp,
    backgroundColor: Color = Color(0x6600021B),
    contentColor: Color = Color(0xFF1C85E8),
    onProgressChanged: ((Int) -> Unit)? = null
) {
    val context = LocalContext.current
    val density = LocalDensity.current
    val img = remember {
        leftDrawable?.let {
            drawableToBitmap(context, it)
        }
    }
    val progress = remember {
        mutableStateOf(progressFirst / 100f)
    }
    val textPaint = remember {
        android.graphics.Paint().apply {
            color = android.graphics.Color.WHITE
            textAlign = android.graphics.Paint.Align.LEFT
            setTextSize(textSize.value * density.density)
        }
    }
    val paint = remember {
        Paint().apply {
            color = backgroundColor
            style = PaintingStyle.Fill
        }
    }

    val contentPaint = remember {
        Paint().apply {
            color = contentColor
            style = PaintingStyle.Fill
        }
    }

    Canvas(
        modifier = Modifier
            .padding(padding)
            .width(width)
            .height(height)
            .pointerInput(Unit) { //点击事件
                detectTapGestures(
                    onPress = { offset ->
                        if (offset.x >= 0 && offset.x <= size.width) {
                            val value =
                                (offset.x - size.height - padding.toPx()) / (size.width * 6 / 8)
                            progress.value = if (value > 1f) 1f else if (value < 0f) 0f else value
                            val integerPercentage: Int = ((progress.value * 100).roundToInt())
                            onProgressChanged?.invoke(integerPercentage)
                        }
                    })
            }
            .pointerInput(Unit) { //拖拽事件
                detectDragGestures(
                    onDrag = { change, _ ->
                        if (change.position.x <= size.width && change.position.x >= 0) {
                            val value =
                                (change.position.x - size.height - padding.toPx()) / (size.width * 6 / 8)
                            progress.value = if (value > 1f) 1f else if (value < 0f) 0f else value
                            val integerPercentage: Int = ((progress.value * 100).roundToInt())
                            onProgressChanged?.invoke(integerPercentage)
                        }

                    }
                )
            }
    ) {
        val paddingPx = padding.toPx()
        val iconSize = size.height - paddingPx * 2 // 进度为零时需要画一个圆直径为 高度减去上下padding
        val calculateLength = size.width * 6 / 8   //进度100所占的长度
        val contentLength = iconSize + calculateLength * progress.value //因为为零时需要一定的宽度
        drawIntoCanvas { canvas ->
            canvas.drawRoundRect(
                0f,
                0f,
                size.width,
                size.height,
                size.height / 2,
                size.height / 2,
                paint
            )
            canvas.drawRoundRect(
                    paddingPx,
                    paddingPx,
                    contentLength + paddingPx,
                    size.height - paddingPx,
                    size.height / 2,
                    size.height / 2,
                    contentPaint
             )
            img?.let {
                canvas.drawImage(
                    image = img.asImageBitmap(),
                    Offset(
                        paddingPx,
                        size.height / 2 - it.height / 2
                    ),
                    contentPaint
                )
            }
            val percentageString = String.format("%.0f%%", progress.value * 100)
            val centerX = size.height - paddingPx / 2 + calculateLength
            val centerY = size.height / 2f - (textPaint.descent() + textPaint.ascent()) / 2
            canvas.nativeCanvas.drawText(percentageString, centerX, centerY, textPaint)
        }

    }
}

这里是传入的是Drawable资源Id(需要注意一下)

fun drawableToBitmap(context: Context, @DrawableRes id: Int): Bitmap? {
    val drawable = ContextCompat.getDrawable(context, id) ?: return null
    return drawable.let {
        val bitmap = Bitmap.createBitmap(
            it.intrinsicWidth, it.intrinsicHeight, Bitmap.Config.ARGB_8888
        )
        val canvas = android.graphics.Canvas(bitmap)
        it.setBounds(0, 0, canvas.width, canvas.height)
        it.draw(canvas)
        canvas.setBitmap(null)
        bitmap
    }
}

不传Drawable资源Id实现效果如下

屏幕截图 2024-04-25 180138.png

继续学习!!!