Jetpack Compose Brush API 详解 —— 新手指南

一、Brush 概述

1.1 什么是 Brush

Brush 是 Jetpack Compose 中用于绘制渐变和纹理的抽象类,它可以应用于文字、形状、边框等各种绘制场景。

1.2 继承关系

kotlin 复制代码
abstract class Brush {
    abstract fun applyTo(paint: Paint, size: Size)
}

// 主要子类:
// 1. SolidColor - 纯色
// 2. LinearGradient - 线性渐变
// 3. RadialGradient - 径向渐变  
// 4. SweepGradient - 扫描渐变
// 5. ShaderBrush - 自定义着色器

二、核心 API 详解

2.1 SolidColor - 纯色画笔

kotlin 复制代码
// 基本用法
Brush.solid(Color.Red)

// 等价于
Color.Red.asBrush()

// 内部实现
class SolidColor(val value: Color) : Brush() {
    override fun applyTo(paint: Paint, size: Size) {
        paint.color = value
        paint.shader = null
    }
}

2.2 LinearGradient - 线性渐变

构造参数详解
kotlin 复制代码
Brush.linearGradient(
    // 必需参数:颜色列表
    colors: List<Color>,
    
    // 可选参数:颜色停止点(0.0-1.0)
    stops: List<Float>? = null,
    
    // 渐变起始点(相对坐标,0-1或具体像素值)
    start: Offset = Offset.Zero,
    
    // 渐变结束点
    end: Offset = Offset.Infinite,
    
    // 渐变平铺模式
    tileMode: TileMode = TileMode.Clamp
)
示例详解
kotlin 复制代码
// 1. 基础水平渐变
Brush.linearGradient(
    colors = listOf(Color.Red, Color.Blue)
    // 默认:start = Offset(0f, 0f), end = Offset(Float.POSITIVE_INFINITY, 0f)
)

// 2. 对角线渐变
Brush.linearGradient(
    colors = listOf(Color.Red, Color.Green, Color.Blue),
    start = Offset(0f, 0f),    // 左上角
    end = Offset(100f, 100f)   // 右下角(像素值)
)

// 3. 带停止点的精确控制
Brush.linearGradient(
    colors = listOf(
        Color.Red,     // 0%
        Color.Yellow,  // 30%
        Color.Green    // 100%
    ),
    stops = listOf(0f, 0.3f, 1f)
)

// 4. 使用相对坐标(0-1范围)
Brush.linearGradient(
    0.0f to Color.Red,
    0.5f to Color.White,
    1.0f to Color.Blue,
    start = Offset(0f, 0.5f),  // 左中
    end = Offset(1f, 0.5f)     // 右中
)

2.3 RadialGradient - 径向渐变

构造参数
kotlin 复制代码
Brush.radialGradient(
    colors: List<Color>,
    center: Offset = Offset.Unspecified,
    radius: Float = Float.POSITIVE_INFINITY,
    stops: List<Float>? = null,
    tileMode: TileMode = TileMode.Clamp
)
示例详解
kotlin 复制代码
// 1. 从中心向外扩散
Brush.radialGradient(
    colors = listOf(Color.Red, Color.Transparent),
    center = Offset(50f, 50f),  // 中心点坐标
    radius = 50f                // 渐变半径
)

// 2. 使用相对坐标
Canvas(modifier = Modifier.size(200.dp)) {
    drawCircle(
        brush = Brush.radialGradient(
            colors = listOf(Color.Yellow, Color.Red),
            center = center,  // 画布中心
            radius = size.minDimension / 2  // 半径 = 宽高最小值的一半
        )
    )
}

// 3. 多色径向渐变
Brush.radialGradient(
    0.0f to Color.White,
    0.3f to Color.Yellow,
    0.6f to Color.Orange,
    1.0f to Color.Red,
    center = Offset(100f, 100f),
    radius = 150f
)

2.4 SweepGradient - 扫描渐变(扇形渐变)

构造参数
kotlin 复制代码
Brush.sweepGradient(
    colors: List<Color>,
    center: Offset = Offset.Unspecified,
    stops: List<Float>? = null,
    tileMode: TileMode = TileMode.Clamp
)
示例详解
kotlin 复制代码
// 1. 基础扫描渐变
Brush.sweepGradient(
    colors = listOf(
        Color.Red,
        Color.Yellow,
        Color.Green,
        Color.Blue,
        Color.Red  // 闭合循环
    ),
    center = Offset(100f, 100f)
)

// 2. 彩虹效果
val rainbowColors = listOf(
    Color.Red,
    Color(0xFFFF7F00), // 橙色
    Color.Yellow,
    Color.Green,
    Color.Blue,
    Color(0xFF4B0082), // 靛蓝
    Color(0xFF9400D3), // 紫色
    Color.Red
)

Brush.sweepGradient(
    colors = rainbowColors,
    center = center
)

2.5 ShaderBrush - 自定义着色器

高级用法
kotlin 复制代码
// 1. 使用 Android 原生 Shader
val customBrush = remember {
    ShaderBrush(
        LinearGradient(
            floatArrayOf(0f, 0f, 100f, 100f),
            intArrayOf(Color.Red.toArgb(), Color.Blue.toArgb()),
            null,
            Shader.TileMode.CLAMP
        )
    )
}

// 2. 创建 Perlin 噪声纹理
val noiseBrush = remember {
    ShaderBrush(
        RuntimeShader("""
            uniform float2 size;
            uniform float time;
            
            half4 main(float2 fragCoord) {
                float2 uv = fragCoord / size;
                float noise = fract(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
                return half4(noise, noise, noise, 1.0);
            }
        """.trimIndent()).apply {
            setFloatUniform("size", 200f, 200f)
        }
    )
}

三、TileMode 详解

3.1 三种平铺模式

kotlin 复制代码
enum class TileMode {
    // 1. Clamp - 边缘颜色延伸
    Clamp,
    
    // 2. Repeated - 重复平铺
    Repeated,
    
    // 3. Mirror - 镜像平铺
    Mirror,
    
    // 4. Decal - 边缘透明(API 31+)
    Decal
}

3.2 示例对比

kotlin 复制代码
// Clamp 模式
Brush.linearGradient(
    colors = listOf(Color.Red, Color.Blue),
    start = Offset(0f, 0f),
    end = Offset(50f, 0f),  // 渐变区域只有50像素
    tileMode = TileMode.Clamp  // 50像素后保持蓝色
)

// Repeated 模式
Brush.linearGradient(
    colors = listOf(Color.Red, Color.Blue),
    start = Offset(0f, 0f),
    end = Offset(50f, 0f),
    tileMode = TileMode.Repeated  // 每50像素重复一次
)

// Mirror 模式
Brush.linearGradient(
    colors = listOf(Color.Red, Color.Blue),
    start = Offset(0f, 0f),
    end = Offset(50f, 0f),
    tileMode = TileMode.Mirror  // 红->蓝->红->蓝 镜像
)

四、性能优化技巧

4.1 Brush 的重用

kotlin 复制代码
// ❌ 错误:每次重组都创建新的 Brush
@Composable
fun MyComponent() {
    Box(
        modifier = Modifier
            .background(
                Brush.linearGradient(colors = listOf(Color.Red, Color.Blue)) // 每次都新建
            )
    )
}

// ✅ 正确:使用 remember 缓存
@Composable
fun MyComponent() {
    val gradientBrush = remember {
        Brush.linearGradient(
            colors = listOf(Color.Red, Color.Blue),
            start = Offset.Zero,
            end = Offset.Infinite
        )
    }
    
    Box(modifier = Modifier.background(gradientBrush))
}

// ✅ 进阶:根据状态变化缓存
val gradientBrush = remember(key1 = colors, key2 = direction) {
    Brush.linearGradient(
        colors = colors,
        start = when(direction) {
            Direction.Horizontal -> Offset.Zero
            Direction.Vertical -> Offset(0f, 0f)
        },
        end = when(direction) {
            Direction.Horizontal -> Offset.Infinite
            Direction.Vertical -> Offset(0f, Float.POSITIVE_INFINITY)
        }
    )
}

4.2 使用 drawWithCache

kotlin 复制代码
Canvas(
    modifier = Modifier
        .size(200.dp)
        .drawWithCache {
            val brush = Brush.radialGradient(
                colors = listOf(Color.Red, Color.Transparent),
                center = center,
                radius = size.minDimension / 2
            )
            onDrawBehind {
                drawCircle(brush)
            }
        }
)

五、高级应用示例

5.1 动态动画渐变

kotlin 复制代码
@Composable
fun AnimatedGradient() {
    var progress by remember { mutableFloatStateOf(0f) }
    
    LaunchedEffect(Unit) {
        while (true) {
            progress = (progress + 0.01f) % 1f
            delay(16) // ~60fps
        }
    }
    
    val animatedBrush by animateBrushAsState(
        targetValue = Brush.sweepGradient(
            colors = listOf(Color.Red, Color.Yellow, Color.Green, Color.Blue),
            center = Offset(100f, 100f)
        ),
        animationSpec = tween(1000)
    )
    
    Canvas(modifier = Modifier.size(200.dp)) {
        rotate(progress * 360) {
            drawCircle(brush = animatedBrush)
        }
    }
}

5.2 渐变蒙版效果

kotlin 复制代码
@Composable
fun GradientMask() {
    BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
        Image(
            painter = painterResource(id = R.drawable.landscape),
            contentDescription = null,
            contentScale = ContentScale.Crop,
            modifier = Modifier.matchParentSize()
        )
        
        // 顶部到中部的渐变蒙版
        Box(
            modifier = Modifier
                .matchParentSize()
                .background(
                    brush = Brush.verticalGradient(
                        0.0f to Color.Black.copy(alpha = 0.8f),
                        0.3f to Color.Black.copy(alpha = 0.3f),
                        0.7f to Color.Transparent,
                        1.0f to Color.Black.copy(alpha = 0.5f)
                    )
                )
        )
    }
}

5.3 自定义渐变形状

kotlin 复制代码
@Composable
fun CustomGradientShape() {
    Canvas(modifier = Modifier.size(300.dp)) {
        // 创建复杂路径
        val path = Path().apply {
            moveTo(size.width * 0.5f, 0f)
            lineTo(size.width, size.height * 0.3f)
            lineTo(size.width * 0.8f, size.height)
            lineTo(size.width * 0.2f, size.height)
            lineTo(0f, size.height * 0.3f)
            close()
        }
        
        // 应用渐变
        drawPath(
            path = path,
            brush = Brush.linearGradient(
                colors = listOf(
                    Color(0xFF667EEA),
                    Color(0xFF764BA2),
                    Color(0xFF6B8DD6),
                    Color(0xFF8E37D7)
                ),
                start = Offset(0f, 0f),
                end = Offset(size.width, size.height)
            )
        )
    }
}

六、常见问题与解决方案

6.1 Brush 不显示或显示异常

kotlin 复制代码
// 问题:Brush 在 small 尺寸下不显示
// 原因:end 使用 Offset.Infinite 但尺寸太小

// 解决方案:使用具体值或相对值
Box(
    modifier = Modifier
        .size(50.dp)
        .background(
            Brush.linearGradient(
                colors = listOf(Color.Red, Color.Blue),
                // end = Offset.Infinite, // ❌ 可能不显示
                end = Offset(50f, 0f) // ✅ 使用具体值
            )
        )
)

6.2 性能问题排查

kotlin 复制代码
// 使用 CompositionLocalProvider 调试
CompositionLocalProvider(
    LocalDensity provides Density(1f) // 强制 1:1 密度
) {
    // 测试 Brush 渲染
}

6.3 内存优化

kotlin 复制代码
// 对于静态渐变,使用 const val
private val STATIC_GRADIENT = Brush.linearGradient(
    colors = listOf(Color.Red, Color.Blue)
)

@Composable
fun StaticComponent() {
    Box(modifier = Modifier.background(STATIC_GRADIENT))
}

七、最佳实践总结

  1. 选择合适的渐变类型:简单渐变用 Linear,圆形用 Radial,环形用 Sweep
  2. 合理使用坐标系统:理解绝对坐标与相对坐标的区别
  3. 缓存 Brush 对象:使用 remember 避免不必要的重建
  4. 注意性能影响:复杂的 Brush 会影响渲染性能
  5. 测试不同配置:在不同屏幕密度和尺寸下测试效果
  6. 利用动画 API:使用 animateBrushAsState 实现平滑过渡

通过深入理解 Brush API 的各个参数和特性,可以创建出丰富、高效的渐变效果,提升应用的视觉体验。

相关推荐
鹿里噜哩40 分钟前
Spring Authorization Server 打造认证中心(二)自定义数据库表
spring boot·后端·kotlin
Huanzhi_Lin44 分钟前
安卓连接夜神模拟器命令及原理
android
AllBlue1 小时前
unity调用安卓方法
android·unity·游戏引擎
PWRJOY1 小时前
Android Studio中安卓模拟器打不开,报错The emulator process for AVD has terminated
android·ide·android studio
凛_Lin~~2 小时前
安卓 Java线程八股文 (线程、多线程、线程池、线程安全)
android·java·开发语言
城东米粉儿2 小时前
关于 PathMeasure 笔记
android
用户815099944112 小时前
浅谈RecyclerView缓存
android
_李小白3 小时前
【Android FrameWork】第二十天:AudioTrack
android·gitee
走在路上的菜鸟3 小时前
Android学Dart学习笔记第十节 循环
android·笔记·学习·flutter