今天大概会聊到的范围
- AnnotatedString
- Text
在 Compose 中显示文字时,我们可以使用 Text 这个 Composable。但没有任何一个文字是单独存在的,所有的文字都会搭配着字型、大小、颜色来显示。
让我们来看看 Text 的 function 定义:
@Composable
fun Text(
text: String,
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 这个 Composable 是一个参数很多的 Composable ,text 和 modifier 算是最好理解的,另外很多参数可以用来调整、美化这个文字。
刚刚提到的字型、大小及颜色,分别就是 fontFamily
代表字型,color
,代表文字颜色。fontSize
就是文字大小。
除了这些之外,fontStyle
就是常见的斜体,可以透过 fontStyle = FontStyle.Italic
设定。
那粗体呢?粗体可以透过 fontWeight
设定。fontWeight = FontWeight.Bold
可以设定成常见的粗体。但 fontWeight
还有不同的级距,从 FontWeight.Thin
~ FontWeight.Black
,同时也代表 FontWeight.W100
~ FontWeight.W900
,数字越大字越粗。
字元间距可以透过 letterSpacing
设定,行高可以透过 lineHeight
设定。如果想要做删除线或底线,可以透过 textDecoration
进行装饰。
如果文字很长时,可以透过 overflow
设定当文字超出框时是要截断还是留三个点、透过 softWrap
决定是否要换行,最後透过 maxLines
设定换行後最多可以有几行。
如果有常用的文字格式,可以透过 TextStyle
将上述说到的各种参数设定好後,在透过 style
参数一次设定整个格式。要注意的是 style
与外部的其他参数同时设定时,会被外部的参数给覆写掉
Text(
text = "AAAAA",
color = Color.Green,
style = TextStyle(
color = Color.Red
)
)
虽然 TextStyle 内有设定
color = Color.Red
,但会被外部的color = Color.Green
覆盖掉,显示绿色
刚刚其实刻意简化了 text
这个参数,text
除了可以接收 String 之外,还能接受 AnnotatedString
。
AnnotatedString
其实很类似原本 android.text 的 SpannableString
。 可以逐字的调整文字的 style。
Text(
text = AnnotatedString(
"BBBBB",
spanStyle = SpanStyle(
color = Color.Green
)
)
)
这样 BBBBB 会显示为绿色
这样好像和直接设定颜色没什麽差异?那 AnnotatedString
的好处到底在哪里呢?
Text(
text =
AnnotatedString(
"BBBBB",
spanStyle = SpanStyle(
color = Color.Green
)
) + AnnotatedString(
"BBBBB",
spanStyle = SpanStyle(
color = Color.Red,
fontWeight = FontWeight.Black
)
)
)
AnnotatedString
可以互相串接。同一个 Text component 内可以有多个不同的 style
Text(
text = buildAnnotatedString {
append(
AnnotatedString("CCCCC", spanStyle = SpanStyle(Color.Red))
)
append(
AnnotatedString("CCCCC", spanStyle = SpanStyle(Color.Green))
)
append(
AnnotatedString("CCCCC", spanStyle = SpanStyle(Color.Blue))
)
}
)
也可以透过 builder 做到一样的事情,将不同的 AnnotatedString 加在一起。也可以 append 没有 style 的文字
Text(
text = buildAnnotatedString {
append(
AnnotatedString("Red", spanStyle = SpanStyle(Color.Red))
)
append(" is not ")
append(
AnnotatedString("Blue", spanStyle = SpanStyle(Color.Blue))
)
}
)
一样是透过 builder,我们可以先设定一个字串,在对特定的位置设定特定的 style。Style 重复设定在一样的位置上。例如这边我想将第一个字和第二个字设定为不同颜色,但是两个字都要有底线:
Text(
text = buildAnnotatedString {
append("Multiple style in one text")
addStyle(
style = SpanStyle(
color = Color.Red,
fontWeight = FontWeight.Bold,
fontStyle = FontStyle.Italic
),
start = 0,
end = 8
)
addStyle(
style = SpanStyle(
color = Color.Blue,
fontFamily = FontFamily.Monospace,
),
start = 9,
end = 14
)
addStyle(
style = SpanStyle(
textDecoration = TextDecoration.Underline
),
start = 0,
end = 14
)
},
)
在使用原本的 Android TextView 时,我们若要做到一样的动作,会需要使用 SpannableString。但是 SpannableString
之所以可以运作,是因为 TextView 的内部,会去判断目前的文字是否是 Spannable ,并且特殊处理。但在 Compose 中,Text 不会去判断 SpannableString,因此无法使用原先在 Android 专案中建立好的 SpannableString。
因此,在使用 Compose 时,会需要重新建构 AnnotatedString。但为了满足好奇,还是研究了一下如何将 SpannableString 转换成 AnnotatedString。
fun SpannableStringBuilder.toAnnotatedString(): AnnotatedString {
// ...
}
我在 SpannableStringBuilder 上建立一个 toAnnotatedString 的 extension function,并预期这个 function 回传 AnnotatedString
data class SpanCollection(val start: Int, val end: Int, val spanStyles: List<CharacterStyle>)
fun SpannableStringBuilder.toAnnotatedString(): AnnotatedString {
var idx = 0
var next: Int
val spanCollections = mutableListOf<SpanCollection>()
while (idx < this.length) {
// find the next span trasition
next = nextSpanTransition(idx, this.length, CharacterStyle::class.java)
// get all spans in this range
val charStyleSpans = getSpans(idx, next, CharacterStyle::class.java)
spanCollections.add(SpanCollection(idx, next, charStyleSpans.toList()))
idx = next
}
// ...
}
在开始组成 AnnotatedString 之前,先透过 while loop 将 SpannableStringBuilder 中的每个 SpanStyle 抓出来,并且将 style 与其影响的位置存放在一个 list 中。
fun SpannableStringBuilder.toAnnotatedString(): AnnotatedString {
var idx = 0
var next: Int
val spanCollections = mutableListOf<SpanCollection>()
while (idx < this.length) {
// find the next span trasition
next = nextSpanTransition(idx, this.length, CharacterStyle::class.java)
// get all spans in this range
val charStyleSpans = getSpans(idx, next, CharacterStyle::class.java)
spanCollections.add(SpanCollection(idx, next, charStyleSpans.toList()))
idx = next
}
return buildAnnotatedString {
append([email protected]())
spanCollections.forEach { spanCollection ->
val (start, end, spans) = spanCollection
spans.forEach {
when (it) {
is ForegroundColorSpan -> {
val span = it
addStyle(
style = SpanStyle(color = Color(span.foregroundColor)),
start = start,
end = end
)
}
// .. other SpanStyle
}
}
}
}
}
最後,透过 builder 将 style 一一加入,建构 AnnotatedString。这边比较麻烦的是要将所有 SpanStyle 列举出来,并逐一转换成对应的 AnnotatedString 的 SpanStyle
Text 可能会是最常用的元件之一。虽然并不是所有的样式都会很常用到,但了解这个最基础元件的用法,也许会在有需要的时候有点帮助。
Reference:
>>: [Day_10]资料储存容器(3) - 字典(dict)
前面已经大概介绍了一下 NiFi 的用途还有特性,那今天就来讲在 NiFi 中,其实是可以对一组 D...
终於讲到树,快接近尾声了(烟 二元搜寻图(Binary Search Tree)是一种很高效的资料结...
听说 JavaScript 的这两大类型「陈述式」与「表达式」也是很基本的观念,瞬间我感觉自己从来没...
系列文章主要是为了练习规律产出,实验性质比较重一点, 内容会根据目前的反馈做出一点改变,欢迎感兴趣的...
若您想用行分隔两个区块的内容,分隔符号区块 正是您需要的工具。 若要新增分隔符号区块,请按一下区块插...