Compose挑燈夜看 - 照亮手機螢幕裡面的書本內容

語言: CN / TW / HK

theme: smartblue

攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第7天,點選檢視活動詳情

一、前言

上一篇文章 Compose回憶童年 - 手拉燈繩-開燈/關燈裡面82年鎢絲燈,讓我又有了新的想法,我們怎麼照亮手機裡面的文字內容呢?

我們會在上一篇文章的基礎上來實現“挑燈夜看”的功能,怎麼下手呢?往下看👇

二、文字著色器

我們想要實現照亮功能,那肯定需要有不亮的文字內容。

通過透明度來可以嗎?肯定不行,文字內容是可以上下滑動的,是一個整體,我們不能通過透明度來做。

在看到小米手機的文字著色效果之後:

小米永珍息屏.png

我知道如何下手了,我先來看看ComposeText如何做漸變著色?

1. 有些同學可能喜歡用Canvas去繪製:

kotlin Canvas(...) { drawIntoCanvas { canvas -> canvas.nativeCanvas.drawText(text, x, y, paint) } } 2. 我們可以使用ModifeirdrawWithCache修飾符,官方文件的連結裡面也給我們了不少示例。

QQ20220830-203813@2x.png

kotlin 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

kotlin 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.png verticalGradient.png

左邊的是linearGradient右邊的是verticalGradient

4444.png 5555.png

左邊的是radialGradient右邊的是sweepGradient

還有一種內建的BrushSolidColor,填充指定顏色。

檢視Brush#LinearGradient原始碼發現它繼承自ShaderBrush

kotlin // 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 kotlin object : ShaderBrush() { override fun createShader(size: Size): Shader { return RadialGradientShader( center = ..., radius = ..., colors = ... ) } ... }

三、實現照亮文字

剛剛上面初始化了一個ShaderBrush,我們照亮文字內容,文字內容不可能只有一屏對吧,肯定需要支援滑動文字,那要怎麼做呢?

我想肯定有掘友知道了,我們可以用ModifierverticalScroll修飾符,記錄滾動狀態ScrollState,然後設定到RadialGradientShadercenter裡面。

我們這裡的文字內容引用了:三國演義的第一章內容,我們同樣需要上一篇文章RopHandleState

kotlin 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

```kotlin val scrollState = ScrollState(0)

private val scrollOffset by derivedStateOf { // 這裡增加Y軸的距離 Offset(size.width / 2F, scrollState.value.toFloat() + size.width * 0.2F) } ``` 可以滾動,我們需要把滾動的距離同步給我們的ShaderBrush

```kotlin // 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:

kotlin val lightContentBrush by derivedStateOf { if(isOpen) { object : ShaderBrush() { ... } } else { SolidColor(Color.DarkGray) } } 這裡需要注意一下,我們在開啟和關閉鎢絲燈的時候,需要把lastScrollOffset設定為初始狀態值

kotlin fun toggle() { isOpen = !isOpen lastScrollOffset = Offset.Zero } 其他相關的程式碼,請參考上一篇文章 Compose回憶童年 - 手拉燈繩-開燈/關燈

我們來看看最終效果吧

2022-08-30 22_18_31.gif

延伸:這裡其實還可通過手指觸控指定範圍區域內高亮哦,有興趣的可以去試試!!