Text
我们知道Android里显示文本使用TextView,如:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" // 文字内容
android:gravity="center" // 文字内容居中显示
android:textColor="#030" // 文字颜色
android:textSize="40sp" // 文字大小
android:textStyle="bold|italic" // 文字样式 加粗和斜体
...
/>
复制代码
显示效果如下图:
那么,Compose如何显示文字呢?其实我们前面的例子中都应该看的很清楚了,我们再来看一下:
@Composable
fun Greeting(name: String, isShowName: Boolean) {
val showName = if (isShowName) "显示名字" else "不显示名字"
Text(text = "Hello $name! $showName")
}
复制代码
对,就这么简单,这里我们使用硬编码的方式,但是我们的编码习惯都是将字符串资源定义到string.xml中供使用,这样Compose和Android View可以共享相同的字符串资源:
@Composable
fun Greeting(isShowName: Boolean) {
val showName = if (isShowName) "显示名字" else "不显示名字"
Text(text = "Hello ${stringResource(id = R.string.compose_coder)}! $showName")
}
@Preview(showBackground = true, widthDp = 150, heightDp = 100)
@Composable
fun DefaultPreview() {
ComposeTheme {
Greeting(true)
}
}
复制代码
在Compose中使用stringResource可以直接读取字符串资源,并通过Preview看到预览效果:
如果我们不需要进行其他设置,直接使用上面的Text就可以展示了文本内容了,但实际开发肯定远远不够的,我们看看Text源码,都有哪些属性:
@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中很多参数,基本上都有默认值,大部分参数之前也都看过,后面我们对这些属性都演示下。
设置文本样式
我们先设置下文字颜色,我们重载一个无参的Greeting方法
@Composable
fun Greeting() {
Text(
text = stringResource(R.string.compose_coder),
color = Color.Red
)
}
复制代码
我们把文字设成了红色,可以看到预览效果。
字号大小
字号大小参数fontSize的类型是TextUnit,我们设置字号大小的时候使用了Int.sp的形式,其实这是Compose为我们写的扩展函数
@Stable
val Int.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
复制代码
Int、Float、Double都可以这样使用,即参数类型为TextUnit我们可以使用这种方式来设置。
斜体
我们将上面的Text修改为斜体:
我们通过FontStyle类设置斜体,看看它的源码:
inline class FontStyle(val value: Int) {
override fun toString(): String {
return when (this) {
Normal -> "Normal"
Italic -> "Italic"
else -> "Invalid"
}
}
companion object {
/** Use the upright glyphs */
val Normal = FontStyle(0)
/** Use glyphs designed for slanting */
val Italic = FontStyle(1)
/** Returns a list of possible values of [FontStyle]. */
fun values(): List<FontStyle> = listOf(Normal, Italic)
}
}
复制代码
代码很简单,只有两个参数Normal和Italic。
字体粗细
我们通过FontWeight来设置字体粗细,我们来看看它的源码:
@Immutable
class FontWeight(val weight: Int) : Comparable<FontWeight> {
companion object {
/** [Thin] */
@Stable
val W100 = FontWeight(100)
/** [ExtraLight] */
@Stable
val W200 = FontWeight(200)
/** [Light] */
@Stable
val W300 = FontWeight(300)
/** [Normal] / regular / plain */
@Stable
val W400 = FontWeight(400)
/** [Medium] */
@Stable
val W500 = FontWeight(500)
/** [SemiBold] */
@Stable
val W600 = FontWeight(600)
// 省略...
复制代码
我们可以看到FontWeight类接收一个Int类型的参数,表示字体的粗细,而且Compose为了方便我们使用,在伴生对象中定义了一些常用的字体粗度,如Bold。我们来设置下看看效果。
这里说明下,FontWeight不只能设置自带的粗细,我们可以使用自定义的值,如:
字体
我们使用fontFamily类设置,同样先看看它的源码:
@Immutable
// TODO Unused parameter canLoadSynchronously
sealed class FontFamily(val canLoadSynchronously: Boolean) {
companion object {
/**
* 默认字体
*/
val Default: SystemFontFamily = DefaultFontFamily()
/**
* 具有低对比度和平淡笔画结尾的字体
*/
val SansSerif = GenericFontFamily("sans-serif")
/**
* Scripts的正式文本
*/
val Serif = GenericFontFamily("serif")
/**
* 字形具有相同固定宽度的字体
*/
val Monospace = GenericFontFamily("monospace")
/**
* 草书,手写字体
*/
val Cursive = GenericFontFamily("cursive")
}
}
复制代码
系统提供默认上面的5种字体,我加上了注释,我们看看它们的区别,我们通过一个竖布局来整体看下:
当然我们可以按照Android那样,添加自定义的字体和字型,把对应的字体文件放到res/font目录下:
放置完成后,需要根据字体文件来定义fontFamily:
字符间距
我们要设置文本中字符中间的间距(空间量),使用的是Text的letterSpacing参数,可以看到这个参数类型和上面说的设置字号大小的一样,都是属于TextUnit,所以我们直接通过”.sp”的方式来设置即可。
文字装饰
通过TextDecoration类设置装饰,如下划线、中划线等,我们来看看它的源码:
@Immutable
class TextDecoration internal constructor(val mask: Int) {
companion object {
@Stable
val None: TextDecoration = TextDecoration(0x0)
/**
* 下划线
*/
@Stable
val Underline: TextDecoration = TextDecoration(0x1)
/**
* 中划线
*/
@Stable
val LineThrough: TextDecoration = TextDecoration(0x2)
// 省略...
}
复制代码
上面TextDecoration可以看到,和前面说到的FontFamily和fontWeight类似,都是在一个类中定义了伴生对象,里面定义了几种常用的格式,我们来通过代码设置看下效果:
文字对齐方式
使用textAlign参数来设置文字的对齐方式。默认情况下,Text会根据其内容选择自然的文字对齐方式;对于从左到右的文字,默认都是左边缘对齐;但对于从右到左的文字,如阿拉伯语或希伯来语,Text容器会右边缘对齐。来看看它的源码:
@Suppress("INLINE_CLASS_DEPRECATED")
inline class TextAlign internal constructor(internal val value: Int) {
override fun toString(): String {
return when (this) {
Left -> "Left"
Right -> "Right"
Center -> "Center"
Justify -> "Justify"
Start -> "Start"
End -> "End"
else -> "Invalid"
}
}
companion object {
/** 左对齐 */
val Left = TextAlign(1)
/** 右对齐 */
val Right = TextAlign(2)
/** 居中对齐 */
val Center = TextAlign(3)
/**
* 内容在容器中按照两端对齐
*/
val Justify = TextAlign(4)
/**
* 将文本与容器的前边缘对齐
*/
val Start = TextAlign(5)
/**
* 将文本与容器的后边缘对齐
*/
val End = TextAlign(6)
/**
* Return a list containing all possible values of TextAlign.
*/
fun values(): List<TextAlign> = listOf(Left, Right, Center, Justify, Start, End)
}
}
复制代码
里面定义了几种对齐方式,我们这里只看看居中的对齐方式,别的方式大家可以自己试验下。
行高
行高通过lineHeight参数设置,它类型也是TextUnit,所以我们和前面设置字号大小、字间距的方式一样:
Text(
text = "我是Compose爱好者",
lineHeight = 80.sp
)
复制代码
文字溢出
处理文字溢出情况,使用overflow参数,类型为TextOverflow。我们看看它的源码:
@Suppress("INLINE_CLASS_DEPRECATED")
inline class TextOverflow internal constructor(internal val value: Int) {
override fun toString(): String {
return when (this) {
Clip -> "Clip"
Ellipsis -> "Ellipsis"
Visible -> "Visible"
else -> "Invalid"
}
}
companion object {
/**
* 基本等同于不做处理,自然被截断,超出部分截断不显示
*/
val Clip = TextOverflow(1)
/**
* 省略号来表示文本已溢出
*/
val Ellipsis = TextOverflow(2)
/**
* 显示省所有文本,如果显示不下,则会显示到屏幕外面
*/
val Visible = TextOverflow(3)
}
}
复制代码
最大行数就不说了,默认值是Int.MaxValue。
文字包含多种样式
文字中包含多种样式非常常见,如相邻字符颜色和字体大小不同等。如果需要再同一个Text可组合项中设置不同的样式,必须使用AnnotatedString,该字符串可使用任意注解样式加以注解。我们来看看它的构造方法:
@Immutable
class AnnotatedString internal constructor(
val text: String,
val spanStyles: List<Range<SpanStyle>> = emptyList(),
val paragraphStyles: List<Range<ParagraphStyle>> = emptyList(),
internal val annotations: List<Range<out Any>> = emptyList()
) : CharSequence {
// 省略...
}
复制代码
它是一个数据类,包含一个String值,用于表示文字内容;一个SpanStyle的list,用于在文本的特定部分指定spanstyle;一个ParagraphStyles的list,指定文字对齐、方向、行高和文字缩进样式。
TextStyle用于Text可组合项,而SpanStyle和ParagraphStyle用于AnnotatedString。ParagraphStyle可用于整个段落,SpanStyle可以在字符级别应用。一旦用ParagraphStyle标记了一部分文字,该部分就会与其余部分隔开,类似Android的SpannableString。
设置文字选择
整体来看Text的使用是不是比Android View中的TextView简单些?我们再来看看怎么设置文字的选择,文字选择就是长按文字时弹出的复制、粘贴等按钮,确定文字的选择范围。
默认情况下,可组合项不可选择,即不支持长按复制选择,如果要选择的话,需要使用SelectionContainer可组合项封装文字元素:
我们可以看到长按文字支持选择复制等操作了。如果我们想文字有些字符不让用户去选择,那么怎么去做呢?我们只需要使用DisableSelection可组合项来封装不可选择的文字部分即可:
我们可以看出圈红色部分的文字我们设置用DisableSelection包裹住后,就不支持长按选择了,其余部分是支持长按选择的。
如果我们需要监听Text的点击次数,可以添加clickable修饰符。如果想获取点击的位置,在对文字的不同部分执行不同的操作情况下,就需要使用clickableText了。
ClickableText(
text = AnnotatedString("点击"),
onClick = { offset ->
Log.v("LM", "$offset")
}
)
复制代码
当用户点击了可组合项,我们如果想向Text值的某一部分附加额外的信息,如特定字符点击跳转到浏览器打开网址,那么我们就可以加一个注解,就是上面说过的AnnotatedString:
val annotatedText = buildAnnotatedString {
append("点击")
pushStringAnnotation(tag = "URL", annotation = "http://www.baidu.com")
withStyle(style = SpanStyle(color = Color.Blue)) {
append("Url")
}
pop() // 结束符
}
ClickableText(
text = annotatedText,
style = TextStyle(fontSize = 30.sp),
onClick = { offest ->
annotatedText.getStringAnnotations(tag = "URL", start = offest, end = offest
).firstOrNull()?.let { annotation ->
Log.v("LM", "click url " + annotation.item)
}
})
复制代码
我们能看到点击“Url”时,能够获取到对应的annotation.item并通过log日志打印出来:
2022-03-19 13:48:07.987 15329-15329/com.carey.compose V/LM: click url http://www.baidu.com
复制代码
好了,今天我们认识了Text的用法和属性设置,如果你也在学习Compose,就点赞收藏下,我们一起学Compose。后面我们会介绍学习其它的控件。