iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0

在看完 UI 外層的 WindowMenuBar 後,接下來持續往內看其他元件。在刻 UI 時,最常需要處理的應該就是「文字」吧?今天的耕讀筆記就以 Text 相關的元件為主軸,研究一下其使用及設定方式。

Text 元件

要在 UI 上顯示文字,不論是一句或是多行,都可以使用 Text 元件。雖然 Text 元件有高達十六個參數可以設定,但除了 text(設定要顯示的文字)參數外,其餘全部都有預設值,所以要在 UI 上放文字只需要非常精簡的一段程式碼:

// 宣告一個文字元件
Text("Hello, World")

// 元件完整參數示意
Text(
    text = "...",
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
)

若是要微調樣式裡的細節,可以透過以上這些參數來做設定:

  • text:設定要顯示的文字。
  • modifier:調整 Text 屬性。
  • color:設定文字顏色。
  • fontSize:設定文字大小。
  • fontStyle:設定文字風格,如斜體。
  • fontWeight:設定文字粗細。
  • fontFamily:設定文字字體。
  • letterSpacing:設定文字的字元間距。
  • textDecoration:設定文字裝飾,如底線。
  • textAlign:設定文字對齊方式。
  • lineHeight:設定文句的行距。
  • overflow:設定當文字字數超過文字框範圍時的效果。
  • softWrap:設定文字是否折行,預設為 true
  • maxLines:設定文字框行數上限。
  • onTextLayout:設定監聽當文字框發生變化時的 Event Handler。
  • style:文字樣式設定。這個設定與上面各屬性重疊,開發者可以選擇偏好的樣式設定方式。

其實 Text 的基礎樣式已遵守 Material Design 的設計原則,若你有客製化的需求,可以用更低階的 BasicText 再做延伸。

調整文字顏色(color

想要調整文字顏色,可以傳入 androidx.compose.ui.graphics.Color 實例當參數。設定 Color 時,可直接傳入 32-bit ARGB 顏色整數值,如 0xFF00FFFF,或是直接用預設色板,如 Color.Cyan

Text(
    text = "...",
    color = Color(0xFF00FFFF)
)

Text(
    text = "...",
    color = Color.Cyan
)

Color 類別的預設色板有:

  • Color.Black:黑色。
  • Color.DarkGray:深灰色。
  • Color.Gray:灰色。
  • Color.LightGray:亮灰色。
  • Color.White:白色。
  • Color.Red:紅色。
  • Color.Green:綠色。
  • Color.Blue:藍色。
  • Color.Yellow:黃色。
  • Color.Cyan:青色。
  • Color.Magenta:品紅色。
  • Color.Transparent:透明。

調整文字大小(fontSize

想要調整文字大小,可直接設定 20.sp 這種較為精簡的語法,或是傳入 androidx.compose.ui.unit.TextUnit 實例當參數。在設定 TextUnit 時,需傳入型別為 Float 的數值(如 20F)及設定 TextUnitType(如 TextUnitType.Sp)。在使用 TextUnit 時,由於用到 Experimental API,記得要標記 @OptIn(ExperimentalUnitApi::class) Annotation。

Text(
    text = "...",
    fontSize = 20.sp
)
Text(
    text = "...",
    fontSize = TextUnit(20F, TextUnitType.Sp)
)

調整文字風格(fontStyle

fontStyle 只有兩種風格:一般(Normal)及斜體(Italic)。想要設定文字風格,可傳入 androidx.compose.ui.text.font.FontStyle 實例當參數,傳入 0 就是一般、傳入 1 就是斜體。或是直接用 Preset 更具可讀性。

Text(
    text = "...",
    fontStyle = FontStyle(0) // 等同 FontStyle.Normal
)
Text(
    text = "...",
    fontStyle = FontStyle.Italic
)

調整文字粗細(fontWeight

想要調整文字粗細,可以傳入 androidx.compose.ui.text.font.FontWeight 實例當參數。設定 FontWeight 時,可直接傳入整數值,或是直接用 Preset。

Text(
    text = "...",
    fontWeight = FontWeight(1000)
)
Text(
    text = "...",
    fontWeight = FontWeight.Thin
)

FontWeight 類別的 Preset 有:

  • Thin
  • ExtraLight
  • Light
  • Normal
  • Medium
  • SemiBold
  • Bold
  • ExtraBold
  • Black
  • W100W200W300W900

調整文字字體(fontFamily

想要設定文字字體,傳入 androidx.compose.ui.text.font.FontFamily 的 Preset 即可。

Text(
    text = "...",
    fontFamily = FontFamily.Default
)
Text(
    text = "...",
    fontFamily = FontFamily.Monospace
)

FontFamily 類別的 Preset 有:

  • Default
  • SansSerif
  • Serif
  • Monospace
  • Cursive

調整文字裝飾(textDecoration

想要設定文字裝飾,傳入 androidx.compose.ui.text.style.TextDecoration 的 Preset 即可。

Text(
    text = "...",
    textDecoration = TextDecoration.Underline
)
Text(
    text = "...",
    textDecoration = TextDecoration.LineThrough
)

TextDecoration 類別的 Preset 有:

  • None:沒有文字裝飾。
  • Underline:底線。
  • LineThrough:刪除線。

調整文字對齊方式(textAlign

想要設定文字對齊方式,傳入 androidx.compose.ui.text.style.TextAlign 的 Preset 即可。

Text(
    text = "...",
    modifier = Modifier.fillMaxWidth(),
    textAlign = TextAlign.Left,
)
Text(
    text = "...",
    modifier = Modifier.fillMaxWidth(),
    textAlign = TextAlign.Center,
)
Text(
    text = "...",
    modifier = Modifier.fillMaxWidth(),
    textAlign = TextAlign.Right
)

在上面的例子裡,傳入 Modifier 參數讓文字框撐大到跟 Window 一樣寬,才能看到文字對齊的視覺效果。關於 Modifier 的用法,之後會有再另外深入研究,現階段先暫時 Copy & Paste 使用即可。TextAlign 類別的 Preset 有:

  • Left:置左對齊。
  • Right:置右對齊。
  • Center:置中對齊。
  • Justify:左右對齊。
  • Start:從頭對齊。
  • End:從尾對齊。

跟文字排版有關的參數(letterSpacinglineHeightoverflowsoftWrapmaxLines

許多時候 UI 顯示的不會只是一小段文字,而是一整篇文章,這時要控制的就是一整段文字,可以把 Text 視為文字框來做更多文字排版的設定。

Text(
    text = "...",
    letterSpacing = 5.sp,
    lineHeight = 10.sp,
    overflow = TextOverflow.Ellipsis,
    softWrap = true,
    maxLines = 3,
)

在上面的範例裡,letterSpacinglineHeight 都是傳入 TextUnit 類別,用 數值 + sp 設定即可。softWrap 設定了當文字超過文字框的範圍時要不要折行,maxLines 是與 overflow 搭配,由 maxLines 決定文字框的高度是幾行,若文字字數超過文字框所能顯示的行數,則由 overflow 決定超過時要怎麼顯示。

TextOverflow 類別的顯示方式共有:

  • Clip:強制截斷。
  • Ellipsis:超過的部份截斷,並在結尾加上 ...
  • Visible:強制顯示,文字會超出文字框破壞版面。

使用 AnnotatedString 排出混合多種樣式及標記的文字

在排版大量文字時,比方說一篇文章,往往會需要混合多種樣式,比方說標題、粗體、底線,甚至想要標記文字裡面的超連結,使用單純的 String 並無法滿足需求。若遇到這樣的情境,我們可以使用 Compose 的 AnnotatedString

AnnotatedString 是一個 Data Class,除了儲存文字內容外,還包含 SpanStyleParagraphStyleRange 清單。SpanStyle 用於標記在字串的文字樣式,ParagraphStyle 用於標記字串的段落樣式,Range 則是確定各字串的範圍。我們可以使用 buildAnnotatedString() 函式,以 DSL 的方式建立 AnnotatedString,並用 append 增加文字進字串,withStyle 則可用於指定文字或段落的樣式。

另外,我們還可以用 pushStringAnnotation() 搭配 tagannotation 參數來增加標記直到呼叫 pop() 結束,這些標記可以用來與點擊事件搭配做互動。

Text(
    text = buildAnnotatedString {
        withStyle(
            style = SpanStyle(
                color = Color.Black,
                fontSize = 30.sp,
            )
        ) {
            append("Click ")
        }

        pushStringAnnotation(
            tag = "URL",
            annotation = "https://kotlin.tips"
        )
        withStyle(
            style = SpanStyle(
                color = Color.Blue,
                fontSize = 30.sp,
                fontWeight = FontWeight.Bold,
                textDecoration = TextDecoration.Underline

            )
        ) {
            append("HERE")
        }

        pop()
    }
)

讓文字可以被點擊

預設 Text 對點擊事件是沒有反應的,若需要做出如超連結的文字點擊互動,可以用 ClickedText。在使用 ClickedText 元件時,傳入的 text 參數就得是 AnnotatedString,再綁定 onClick 事件,就可以做出超連結的效果:

val annotatedText = buildAnnotatedString { /* ... */ }

ClickableText(
    text = annotatedText,
    onClick = { offset ->
        annotatedText.getStringAnnotations(
            tag = "URL",
            start = offset,
            end = offset
        ).firstOrNull()?.let { annotation ->
                println("Go to ${annotation.item}")
            }
    }
)

讓文字可選取或不可選取

Compose 元件是可以互相堆疊與組合使用的,由於許多地方都會用到 Text 元件(比方說在 Button 裡),因此預設 Text 是不能被選取的(防止按鈕上的文字也會被選取,影響其他按鈕互動)。不過在一些情境下,比方說顯示一整篇的文章時,我們會需要讓 Text 裡的文字可以被選取。

要讓 Text 的文字可以被選取,只要在外層加一個 SelectionContainer 即可,只要被這個容器包住的 Text 元件就能夠被選取。反之,若不想元件內的 Text 被選取,則可用 DisableSelection 包住。甚至這兩個容器還可以互動包夾混用:

Column {
    SelectionContainer {
        Column {
            Text("I'm SELECTABLE")
            DisableSelection {
                Text(
                    text = "I'm NOT selectable"
                )
            }
            Text("I'm SELECTABLE")
        }
    }
    
    Text(
        text = "I'm NOT selectable"
    )
}

從上例可以看出,單獨的 Text 是不能被選取的,而被 SelectionContainer 包住的 Text 則可以,但在 SelectionContainer 容器裡被 DisableSelection 包住的 Text 也是不能被選取的。透過這兩個容器的運用,就可以明確界定哪些文字可選取,哪些不行了。

參考資料


上一篇
第 8 天:常用 UI 元件之 MenuBar
下一篇
第 10 天:常用 UI 元件之 TextField
系列文
傳教士的 Compose for Desktop 耕讀筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
John Lu
iT邦新手 3 級 ‧ 2022-09-16 00:26:39

這跟本 Text 的百科全書吧! 推~

聖佑 iT邦新手 4 級 ‧ 2022-09-17 16:01:20 檢舉

因為還在初學,所以只好地毯式的掃過一次各元件,進階的還要靠你多分享了 ?

我要留言

立即登入留言