一、前言
上一篇文章 Compose回忆童年 - 手拉灯绳-开灯/关灯里面82年钨丝灯,让我又有了新的想法,我们怎么照亮手机里面的文本内容呢?
我们会在上一篇文章的基础上来实现“挑灯夜看”的功能,怎么下手呢?往下看👇
二、文本着色器
我们想要实现照亮功能,那肯定需要有不亮的文本内容。
通过透明度来可以吗?肯定不行,文本内容是可以上下滑动的,是一个整体,我们不能通过透明度来做。
在看到小米手机的文本着色效果之后:
我知道如何下手了,我先来看看Compose
的Text
如何做渐变着色?
1. 有些同学可能喜欢用Canvas
去绘制:
Canvas(...) {
drawIntoCanvas { canvas ->
canvas.nativeCanvas.drawText(text, x, y, paint)
}
}
2. 我们可以使用Modifeir的drawWithCache修饰符,官方文档的链接里面也给我们了不少示例。
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-beta01的API变更里面,向TextStyle
和SpanStyle
添加了 Brush
API,以提供使用渐变颜色绘制文本的方法。
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、右边的是verticalGradient
左边的是radialGradient、右边的是sweepGradient
还有一种内置的Brush,SolidColor,填充指定颜色。
查看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,我们照亮文本内容,文本内容不可能只有一屏对吧,肯定需要支持滑动文本,那要怎么做呢?
我想肯定有掘友知道了,我们可以用Modifier的verticalScroll修饰符,记录滚动状态ScrollState,然后设置到RadialGradientShader的center
里面。
我们这里的文本内容引用了:三国演义的第一章内容,我们同样需要上一篇文章的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回忆童年 - 手拉灯绳-开灯/关灯
我们来看看最终效果吧:
延伸:这里其实还可通过手指触摸指定范围区域内高亮哦,有兴趣的可以去试试!!