Compose 自定义 - 绘制 Draw

一、概念

  • 所有的绘制操作都是通过调整像素大小来执行的。若要确保项目在不同的设备密度和屏幕尺寸上都能采用一致的尺寸,请务必使用 .toPx() 对 dp 进行转换或者采用小数尺寸。 

二、Modifier 修饰符绘制

官方页面

在修饰的可组合项之上或之下绘制。

.drawWithContent

fun Modifier.drawWithContent(
    onDraw: ContentDrawScope.() -> Unit
)

在 Lambda 中调用 drawContent() 就是绘制所修饰的内容,由此控制先后顺序,后绘制的会显示在上面。

.drawBehind

fun Modifier.drawBehind(
    onDraw: DrawScope.() -> Unit
)

修饰的内容会显示在 Lambda 内容之上(底层是先绘制 Lambda 内容再绘制所修饰的内容,后绘制的会显示在上面)。

.drawWithCache

fun Modifier.drawWithCache(
    onBuildDrawCache: CacheDrawScope.() -> DrawResult
)

当绘制复杂效果时,不希望因为重组而重新创建 Lambda 中用于绘制的实例如 Bush、Path 等,这可能会产生内存抖动。在 Lambada 中调用 onDrawWithContent()、onDrawBehind() 就类似于上面两个修饰符的功能。

 Compose 自定义 - 绘制 Draw_第1张图片

@Composable
fun Demo() {
    Row(
        modifier = Modifier.size(150.dp),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Image(
            painterResource(id = R.drawable.logo_wechat_square),
            contentDescription = null,
            modifier = Modifier
                .size(50.dp)
                .drawWithContent {
                    drawContent()
                    drawRedDot()    //后绘制的会显示在上面
                }
        )
        Image(
            painterResource(id = R.drawable.logo_wechat_square),
            contentDescription = null,
            modifier = Modifier.padding(start = 10.dp).size(50.dp).drawBehind {
                drawRedDot()
            }
        )
    }
}

fun DrawScope.drawRedDot() {
    drawCircle(
        color = Color.Red,
        radius = 18F,
        center = Offset(drawContext.size.width, 0f)
    )
}

三、Canvas() 可组合项绘制

是一个可组合项。Compose 作为跨平台 UI 框架,所使用的 Canvas() 函数只是一个封装,最终还是调用具体平台即 Android 原生的 Canvas。

fun Canvas(

        modifier: Modifier,

        onDraw: DrawScope.() -> Unit

) = Spacer(modifier.drawBehind(onDraw))

发现该方法只是一个封装,真正绘制的是调用 drawBehind()。绘制内容是显示在 Spacer 下面的,由于 Spacer 是透明的,因此我们所绘制内容得以全部显示。

四、Brush

官方页面

用于绘制颜色(只指定一种颜色就是纯色)。

linearGradient

线性渐变

fun linearGradient(
        colors: List,        //渐变颜色
        start: Offset = Offset.Zero,        //开始的位置
        end: Offset = Offset.Infinite,        //结束的位置
        tileMode: TileMode = TileMode.Clamp        //重复模式
): Brush

水平渐变和垂直渐变底层就是调用的线性渐变。

Compose 自定义 - 绘制 Draw_第2张图片

horizontalGradient

水平方向渐变

fun horizontalGradient(
        colors: List,
        startX: Float = 0.0f,
        endX: Float = Float.POSITIVE_INFINITY,
        tileMode: TileMode = TileMode.Clamp
): Brush
Compose 自定义 - 绘制 Draw_第3张图片

verticalGradient

垂直方向渐变

fun verticalGradient(
        colors: List,
        startY: Float = 0.0f,
        endY: Float = Float.POSITIVE_INFINITY,
        tileMode: TileMode = TileMode.Clamp
): Brush
Compose 自定义 - 绘制 Draw_第4张图片

radialGradient

放射渐变

fun radialGradient(
        colors: List,
        center: Offset = Offset.Unspecified,        //中心位置
        radius: Float = Float.POSITIVE_INFINITY,        //半径
        tileMode: TileMode = TileMode.Clamp
): Brush
Compose 自定义 - 绘制 Draw_第5张图片

sweepGradient

扫描渐变

fun sweepGradient(
        colors: List,
        center: Offset = Offset.Unspecified
): Brush
Compose 自定义 - 绘制 Draw_第6张图片

4.2.1 使用 colorStop 更改颜色分布

自定义颜色在渐变中的显示方式,可以调整每种颜色的 colorStop 值,0 ~ 1 之间的小数。

Compose 自定义 - 绘制 Draw_第7张图片

val colorStops = arrayOf(
    0.0f to Color.Yellow,
    0.2f to Color.Red,
    1f to Color.Blue
)
Box(modifier = Modifier
        .requiredSize(200.dp)
        .background(Brush.horizontalGradient(colorStops = colorStops))
)

 4.2.2 使用 TileMode 让图案重复显示

当未指定 Brush 的开始位置 start 和结束位置 end 时,默认会填满整个区域,只有在区域 > Brush 时 TileMode 才会在渐变中平铺。以下举例 HorizontalGradient 的效果。

TileMode.Repeated 将区域剩余空间绘制为重复的顺序颜色。 Compose 自定义 - 绘制 Draw_第8张图片
TileMode.Mirror 将区域剩余空间绘制为重复的反转颜色。 Compose 自定义 - 绘制 Draw_第9张图片
TileMode.Clamp 将区域剩余空间绘制为结束颜色。 Compose 自定义 - 绘制 Draw_第10张图片
TileMode.Decal 将区域剩余空间绘制为透明色。(仅适用于 API 31 及更高版本。可使用 TileMode.isSupported() 确定设备是否支持 TileMode。如果使用了不受支持的 TileMode,系统会应用默认的 TileMode.Clamp。) Compose 自定义 - 绘制 Draw_第11张图片

 4.2.3 更改 Brush 大小

当知道绘制区域大小时(如在 DrawScope 中通过 size 获取)可以按照 TileMode 方式平铺,在不知道的情况下(如将 Brush 分配给文字)可以扩展 Shader 重写 createShader() 函数利用绘制区域大小 size 形参。对于 radialGradient 如果未指定中心位置 center 和半径radius,渐变将占据整个 DrawScope 但是是以宽高较小的那边为直径,此时自定义大小会获得更好的效果(发散到屏幕外边去)。

Compose 自定义 - 绘制 Draw_第12张图片

val listColors = listOf(Color.Yellow, Color.Red, Color.Blue)
val customBrush = remember {
    object : ShaderBrush() {
        override fun createShader(size: Size): Shader {
            return LinearGradientShader(
                colors = listColors,
                from = Offset.Zero,
                to = Offset(size.width / 4f, 0f),
                tileMode = TileMode.Mirror
            )
        }
    }
}
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(customBrush)
)

4.2.4 使用图片作为 Brush

如需使用 ImageBitmap 作为 Brush,请以 ImageBitmap 的形式加载相应图片,然后创建 ImageShader Brush。可以应用于一下几种类型的绘制:背景、文字、画布。

Compose 自定义 - 绘制 Draw_第13张图片

val imageBrush =
    ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog)))

//用于 background
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(imageBrush)
)

//用于 TextStyle
Text(
    text = "Hello Android!",
    style = TextStyle(
        brush = imageBrush,
        fontWeight = FontWeight.ExtraBold,
        fontSize = 36.sp
    )
)

//用于 DrawScope#drawCircle()
Canvas(onDraw = {
    drawCircle(imageBrush)
}, modifier = Modifier.size(200.dp))

五、DrawScope

官方页面

在 DrawScope 中,可以访问到 drawContext 成员,它存储了以下信息:绘制尺寸size、封装的canvas、用来旋转缩放移动的transform,而通过 canvas.nativeCanvas 就能获取具体平台的实现,即可以调用 Android 原生的 Canvas 来实现更多需求。

绘制 drawLine 画线
drawRect 画矩形
drawRoundRect 画圆角矩形
drawImage 绘制图片
drawCircle 画圆形
drawOval 画椭圆形
drawArc 画弧度跟扇形
drawPath 画路径
drawPoints 画点
行为 inset 将DrawScope坐标空间平移
translate 平移坐标

rotate(旋转坐标)讲的是旋转了多少角度

rotateRad(旋转坐标)讲的是旋转了多少弧度

scale 缩放坐标
clipRect 裁剪矩形区域,绘制在裁剪好的矩形区域内。ClipOp.Difference从当前剪辑中减去提供的矩形。
clipPath 裁剪路径
drawIntoCanvas 直接提供底层画布
withTransform 组合转换

5.1 绘制

5.1.1 画线 drawLine()

fun drawLine(
        color: Color,
        start: Offset,
        end: Offset,
        strokeWidth: Float = Stroke.HairlineWidth,
        cap: StrokeCap = Stroke.DefaultCap,
        pathEffect: PathEffect? = null,
        /*FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )

cap

线条两头的形状

StrokeCap.Butt 平的(默认)
StrokeCap.Square 也是平的但是长一截
StrokeCap.Round 圆的

pathEffect

线条效果

PathEffect.cornerPathEffect(radius: Float)

将线段之间的锐角替换为指定半径的圆角 radius是半径

PathEffect.dashPathEffect(intervals: FloatArray, phase: Float = 0f)

将形状绘制为具有给定间隔的一系列破折号。比如虚线 例如interval={20,5},第一个参数表示虚线的长度是20,5是虚线之间的间隔是5. phase 偏移

PathEffect.chainPathEffect(outer: PathEffect, inner: PathEffect)

创建一个PathEffect,将内部效果应用于路径,然后应用外部效果

PathEffect.stampedPathEffect(shape: Path, advance: Float, phase: Float,style: StampedPathEffectStyle)

用path表示的指定形状冲压绘制的路径.  shape要踩踏的路径,advance 每个冲压形状之间的前进间距, phase 在压印第一个形状之前要偏移的相位量, style如何在每个位置转换形状,因为它是冲压. style有三种取值 StampedPathEffectStyle.Translate 平移 ,StampedPathEffectStyle.Rotate 旋转,StampedPathEffectStyle.Morph 变形

Compose 自定义 - 绘制 Draw_第14张图片 

5.2 行为

5.2.1 缩放 scale()

5.2.2 平移 translate()

5.2.3 旋转 rotate()

5.2.4 

你可能感兴趣的:(Compose,android)