Compose 简单自定义SeekBar
背景
抱着学习的心态,想了解一下Compose如何实现自定义View。刚好最近需要实现一个简单的进度条用Compose实现一下。
实现
@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实现效果如下
继续学习!!!