Jetpack Compose 如何在Canvas绘制带斜纹图案的线性进度条
方案实现
推荐方案:纯代码生成斜纹填充(无需额外资源,全Compose实现)
你原本的drawLine方法不需要大改,只需要将传入的color参数替换为brush参数,通过自定义Shader生成斜纹效果即可,不需要依赖任何XML资源或者drawable文件。
首先实现斜纹Brush的生成方法:
fun createDiagonalStripeBrush( stripeColor: Color, gapColor: Color = Color.White, stripeWidth: Dp = 4.dp, gapWidth: Dp = 3.dp, angle: Float = 45f ): Brush { // 兼容API 33以下的备用实现如果不需要兼容可以直接用Runtime Shader return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ShaderBrush( object : Shader() { override fun execute(inputs: ShaderInputs, size: FloatSize): ShaderResult { val totalTileWidth = (stripeWidth + gapWidth).toPx() val radians = Math.toRadians(angle.toDouble()).toFloat() val cos = cos(radians) val sin = sin(radians) return ShaderResult { x, y, _ -> val projectedPos = x * cos + y * sin if (projectedPos % totalTileWidth < stripeWidth.toPx()) { stripeColor } else { gapColor } } } } ) } else { // 低版本兼容实现:通过绘制重复条纹生成Bitmap再平铺 val tileSizePx = (stripeWidth + gapWidth).toPx().toInt() * 4 val bitmap = ImageBitmap(tileSizePx, tileSizePx) val canvas = Canvas(bitmap) val paint = Paint().apply { color = stripeColor } val radians = Math.toRadians(angle.toDouble()).toFloat() val offsetStep = (stripeWidth + gapWidth).toPx() var currentOffset = -tileSizePx.toFloat() while (currentOffset < tileSizePx * 2) { canvas.save() canvas.rotate(angle, tileSizePx / 2f, tileSizePx / 2f) canvas.drawRect( x = currentOffset, y = 0f, width = stripeWidth.toPx(), height = tileSizePx.toFloat(), paint = paint ) canvas.restore() currentOffset += offsetStep } ImageBrush( imageBitmap = bitmap, tileModeX = TileMode.Repeated, tileModeY = TileMode.Repeated ) } }
然后修改你原有的绘制代码即可:
// 按需调整参数匹配设计稿 val stripeProgressBrush = createDiagonalStripeBrush( stripeColor = progressColor, gapColor = Color.White, stripeWidth = 3.dp, gapWidth = 2.dp, angle = -45f ) drawLine( brush = stripeProgressBrush, // 替换原有的color参数 start = Offset(if (whiteGap) progressCapWidth else startOffsetBg, yOffset), end = Offset(endOffsetProgress, yOffset), strokeWidth = progressHeight.toPx(), cap = if (roundedCorners) StrokeCap.Round else StrokeCap.Butt, )
可选方案:使用drawable平铺
如果已经有现成的斜纹drawable资源,也可以直接转成平铺Brush使用,不需要XML布局:
val context = LocalContext.current val stripeDrawable = ContextCompat.getDrawable(context, R.drawable.your_stripe_res)!! val stripeBitmap = stripeDrawable.toBitmap().asImageBitmap() val tiledBrush = ImageBrush( imageBitmap = stripeBitmap, tileModeX = TileMode.Repeated, tileModeY = TileMode.Repeated ) // 同样传入drawLine的brush参数即可 drawLine( brush = tiledBrush, // 其余参数保持原有逻辑不变 )
该方案需要适配不同屏幕密度的drawable资源,灵活度低于纯代码生成方案,优先推荐第一种实现。
内容的提问来源于stack exchange,提问作者daniel-eh
相关产品推荐
相关产品推荐

