注册

Compose挑灯夜看 - 照亮手机屏幕里面的书本内容

一、前言

上一篇文章 Compose回忆童年 - 手拉灯绳-开灯/关灯里面82年钨丝灯,让我又有了新的想法,我们怎么照亮手机里面的文本内容呢?

我们会在上一篇文章的基础上来实现“挑灯夜看”的功能,怎么下手呢?往下看👇

二、文本着色器

我们想要实现照亮功能,那肯定需要有不亮的文本内容。

通过透明度来可以吗?肯定不行,文本内容是可以上下滑动的,是一个整体,我们不能通过透明度来做。

在看到小米手机的文本着色效果之后:

小米万象息屏.png

我知道如何下手了,我先来看看ComposeText如何做渐变着色?

1. 有些同学可能喜欢用Canvas去绘制:

Canvas(...) {
drawIntoCanvas { canvas ->
canvas.nativeCanvas.drawText(text, x, y, paint)
}
}

2. 我们可以使用ModifeirdrawWithCache修饰符,官方文档的链接里面也给我们了不少示例。

QQ20220830-203813@2x.png

Text(
text = "永远相信美好的事情即将发生❤️",
modifier = Modifier
.graphicsLayer(alpha = 0.99f)
.drawWithCache {
val brush = Brush.horizontalGradient(
listOf(
Color(0xFFE24CE2),
Color(0xFF73BB70),
Color(0xFFE24CE2)
)
)
onDrawWithContent {
drawContent()
drawRect(brush, blendMode = BlendMode.SrcAtop)
}
}
)

上面代码,我们使用到了BlendMode,我们这里用的是BlendMode#SrcAtop: 将源图像合成到目标图像上,仅限于与目标重叠的位置,确保只有文本可见并且矩形的其余部分被剪切。

3. Google在Compose1.2.0-beta01API变更里面,向TextStyleSpanStyle添加了 Brush API,以提供使用渐变颜色绘制文本的方法。

兄弟们支持了吗.png

private val GradientColors = listOf(
Color(0xFF00FFFF), Color(0xFF97E063),
Color(0xFFE24CE2), Color(0xFF97E063)
)
Text(
modifier = Modifier.align(Alignment.Center).requiredWidthIn(max = 250.dp),
text = "永远相信美好的事情即将发生❤️,我们不会期待米粉的期待!\n\n兄弟们支持了吗?",
style = TextStyle(
brush = Brush.linearGradient(
colors = GradientColors
)
)
)

我们可以看到Emoji表情没有被着色,非常Nice。

我们看一下linearGradient/verticalGradient/radialGradient/sweepGradient效果对比:

linearGradient.pngverticalGradient.png

左边的是linearGradient右边的是verticalGradient

4444.png5555.png

左边的是radialGradient右边的是sweepGradient

还有一种内置的BrushSolidColor,填充指定颜色。

查看Brush#LinearGradient源码发现它继承自ShaderBrush

// androidx.compose.ui.graphics.Brush
class LinearGradient internal constructor(
private val colors: List<Color>,
private val stops: List<Float>? = null,
private val start: Offset,
private val end: Offset,
private val tileMode: TileMode = TileMode.Clamp
) : ShaderBrush()

自定义ShaderBrush,可以修改画笔大小,那么我们也来整一个,用于下面的钨丝灯的照亮效果,刚刚上面还介绍了到一个gradient符合我们的要求,radialGradient,更多的源码细节,这里就不做深入介绍,夜深了哈哈哈。

我们接下来需要初始化一个ShaderBrush

object : ShaderBrush() {
override fun createShader(size: Size): Shader {
return RadialGradientShader(
center = ...,
radius = ...,
colors = ...
)
}
...
}

三、实现照亮文本

刚刚上面初始化了一个ShaderBrush,我们照亮文本内容,文本内容不可能只有一屏对吧,肯定需要支持滑动文本,那要怎么做呢?

我想肯定有掘友知道了,我们可以用ModifierverticalScroll修饰符,记录滚动状态ScrollState,然后设置到RadialGradientShadercenter里面。

我们这里的文本内容引用了:三国演义的第一章内容,我们同样需要上一篇文章RopHandleState

private fun ComposeText(state: RopeHandleState) {
Text(
text = sanguoString,
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(state.scrollState),
style = LocalTextStyle.current.merge(
TextStyle(
fontSize = 18.sp,
brush = state.lightContentBrush
)
)
)
}

这里我们用到了TextStyle#Brush的API,同时也添加了滚动修饰符,因为我们需要上下滑动文本,保证“钨丝灯”能照亮我们的文本内容。

我们在RopHandleState里面初始化ScrollState

val scrollState = ScrollState(0)

private val scrollOffset by derivedStateOf {
// 这里增加Y轴的距离
Offset(size.width / 2F, scrollState.value.toFloat() + size.width * 0.2F)
}

可以滚动,我们需要把滚动的距离同步给我们的ShaderBrush

// isOpen == true,钨丝灯亮了需要初始化ShaderBrush
object : ShaderBrush() {
override fun createShader(size: Size): Shader {
lastScrollOffset = Offset(size.width/2F, scrollOffset.y)
return RadialGradientShader(
center = lastScrollOffset!!,
radius = size.minDimension,
colors = listOf(Color.Yellow, Color(0xff85733a), Color.DarkGray)
)
}
override fun equals(other: Any?): Boolean {
return lastScrollOffset?.y == scrollOffset.y
}
}

// isOpen == false,钨丝灯灭了
SolidColor(Color.DarkGray)

根据“钨丝灯”的状态,返回不同的Brush:

val lightContentBrush by derivedStateOf {
if(isOpen) {
object : ShaderBrush() { ... }
} else {
SolidColor(Color.DarkGray)
}
}

这里需要注意一下,我们在打开和关闭钨丝灯的时候,需要把lastScrollOffset设置为初始状态值

fun toggle() {
isOpen = !isOpen
lastScrollOffset = Offset.Zero
}

其他相关的代码,请参考上一篇文章 Compose回忆童年 - 手拉灯绳-开灯/关灯

我们来看看最终效果吧

2022-08-30 22_18_31.gif

延伸:这里其实还可通过手指触摸指定范围区域内高亮哦,有兴趣的可以去试试!!


作者:Halifax
链接:https://juejin.cn/post/7137673203714899998
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册