Compose Text简单使用

4,427 阅读5分钟

Text控件的相关API说明

Compose中的Text就等价于Android原生中的TextView,API也比较简单:

fun Text(
    text: String, // 文字内容,可以直接传递字符串,也可以使用stringResource(id = R.string.hello)来指定
    modifier: Modifier = Modifier, // 修饰符,可以指定宽高,背景,点击事件等。
    color: Color = Color.Unspecified, // 文字颜色
    fontSize: TextUnit = TextUnit.Unspecified, // 文字大小
    fontStyle: FontStyle? = null, // 文字样式,比如斜体
    fontWeight: FontWeight? = null, // 字体宽度,比如粗体
    fontFamily: FontFamily? = null, // 字体样式,比如SansSerif,Serif等
    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 // 设置Style,类似TextView的style
)

TextStyle的API,内容跟Text里面的大部分相同,具体可以查看相关API,这里有个点需要提一下: Text不能垂直居中,要想垂直居中,可以在外面包一层父View,比如Box

基础示例

我们来个小Demo

@Composable
fun TextDemo() {
    val text = "this is compose text demo, which likes TextView in android native xml layout"
    Text(
        text = text, // 文字
        color = Color.Green, // 字体颜色
        fontSize = 16.sp, // 字体大小
        fontStyle = FontStyle.Italic, // 斜体
        fontWeight = FontWeight.Bold, // 粗体
        textAlign = TextAlign.Center, // 对齐方式: 居中对齐
        modifier = Modifier.width(300.dp), // 指定宽度为300dp
        maxLines = 2, // 最大行数
        overflow = TextOverflow.Ellipsis, // 文字溢出后就裁剪
        softWrap = true, // 文字过长时是否换行
        textDecoration = TextDecoration.Underline, // 文字装饰,这里添加下划线
    )
}

效果如下:

示例

然后我们加上字体样式:

fontFamily = FontFamily.Cursive, // 字体样式

效果如下:

示例

我们再加上行高和字符间距:

lineHeight = 40.sp, // 行高40sp
letterSpacing = 5.sp // 字符间距5sp

效果如下:

示例

富文本

使用原生的TextView如果想要实现富文本,需要使用Spanable,而且需要计算文字的下标,非常麻烦,Compose的就相当好用了。

1 使用SpanStyle来实现富文本

API如下:

class SpanStyle(
    val color: Color = Color.Unspecified, // 文字颜色
    val fontSize: TextUnit = TextUnit.Unspecified, // 文字大小
    val fontWeight: FontWeight? = null, // 字体粗细,比如粗体
    val fontStyle: FontStyle? = null, // 文字样式,比如斜体
    val fontSynthesis: FontSynthesis? = null, // 指定的字体找不到时,所采用的策略
    val fontFamily: FontFamily? = null, //  字体样式,比如Serif
    val fontFeatureSettings: String? = null, // 字体的排印设置,可以取CSS中font-feature-settings的值
    val letterSpacing: TextUnit = TextUnit.Unspecified, // 字符间距
    val baselineShift: BaselineShift? = null, // 文字举例baseline的向上偏移量
    val textGeometricTransform: TextGeometricTransform? = null, // 用于几何变换,比如缩放、倾斜等
    val localeList: LocaleList? = null, // 国际化相关符号列表
    val background: Color = Color.Unspecified, // 背景色
    val textDecoration: TextDecoration? = null, // 装饰,比如下划线
    val shadow: Shadow? = null // 阴影
) 

直接看Demo:

@Composable
fun TextDemo2() {
    Text(buildAnnotatedString {
        // 使用白色背景,红色字体,18sp,Monospace字体来绘制"Hello " (注意后面有个空格)
        withStyle(style = SpanStyle(color = Color.Red, background = Color.White, fontSize = 18.sp, fontFamily = FontFamily.Monospace)) {
            append("Hello ")
        }
        // 正常绘制"World"
        append("World ")
        // 使用黄色背景,绿色字体,18sp,Serif字体,W900粗体来绘制"Click"
        withStyle(style = SpanStyle(color = Color.Green, background = Color.Yellow, fontSize = 30.sp, fontFamily = FontFamily.Serif, fontWeight = FontWeight.W900)) {
            append("Click")
        }
        // 正常绘制" Me" (注意前面有个空格)
        append(" Me")

        // 添加阴影及几何处理
        withStyle(
            style = SpanStyle(
                color = Color.Yellow,
                background = Color.White,
                baselineShift = BaselineShift(1.0f), // 向BaseLine上偏移10
                textGeometricTransform = TextGeometricTransform(scaleX = 2.0F, skewX = 0.5F), // 水平缩放2.0,并且倾斜0.5
                shadow = Shadow(color = Color.Blue, offset = Offset(x = 1.0f, y = 1.0f), blurRadius = 10.0f) // 添加音阴影和模糊处理
            )
        ) {
            append(" Effect")
        }
    })
}

其中buildAnnotatedString()可以理解为构建了一个作用域,在该作用域内可以使用withStyle(style)来指定文字格式,效果如下:

示例

2 使用ParagraphStyle来实现段落

API如下:

class ParagraphStyle constructor(
    val textAlign: TextAlign? = null, // 对齐方式
    val textDirection: TextDirection? = null, // 文字方向
    val lineHeight: TextUnit = TextUnit.Unspecified, //行高
    val textIndent: TextIndent? = null // 缩进方式
)

直接看Demo:

@Composable
fun TextDemo3() {
    Text(buildAnnotatedString {
        // 指定对齐方式为Start,通过textIndent指定第一行每段第一行缩进32sp,其余行缩进8sp
        withStyle(style = ParagraphStyle(textAlign = TextAlign.Start, textIndent = TextIndent(firstLine = 32.sp, restLine = 8.sp))) {

            // 第一段,因为只有一行,所以直接缩进32sp
            withStyle(style = SpanStyle(color = Color.Red)) {
                append("Hello, this is first paragraph\n")
            }
            // 第二段(第一行会缩进32sp,后续每行会缩进8sp)
            withStyle(style = SpanStyle(color = Color.Green, fontWeight = FontWeight.Bold)) {
                append("Hello, this is second paragraph,very long very long very long very long very long very long very long very long very long very long\n")
            }
            // 第三段,因为只有一行,所以直接缩进32sp
            append("Hello, this is third paragraph\n")
        }
    })
}

效果如下:

示例

交互

传统的Android的TextView可以实现选中/不可选中,但是却很难实现部分可选中,部分不可选中;传统的TextView可以设置点击事件,但是很难实现获取点击文字的位置,这些在Compose中都不是事。

1 可选中和不可选中

我们可以直接使用SelectionContainer来包括可以选中的文本,使用DisableSelection来包括不可选中的文本,eg:

@Composable
fun TextDemo4() {
    // 设置可选区域
    SelectionContainer {
        // Column等价于竖直的LinearLayout
        Column {
            Text(text = "可以选中我,可以选中我,可以选中我")

            // 设置不可选区域
            DisableSelection {
                Text(text = "选不中我,选不中我,选不中")
            }

            // 位于可选区域内,可选
            Text(text = "可以选中我,可以选中我,可以选中我")
        }
    }
}

效果如下:

示例

2 单个文字响应点击事件

我们可以直接使用ClickableText来实现点个文字的点击效果,API如下:

fun ClickableText(
    text: AnnotatedString, // 传入的文字,这里必须传入AnnotatedString
    modifier: Modifier = Modifier, // 修饰符
    style: TextStyle = TextStyle.Default, // 文本Style
    softWrap: Boolean = true, // 文本长度过长时,是否换行
    overflow: TextOverflow = TextOverflow.Clip, // 文字超出显示范围的处理方式,默认Clip,就是不显示
    maxLines: Int = Int.MAX_VALUE, // 最大行数
    onTextLayout: (TextLayoutResult) -> Unit = {}, // 布局发生变化的回调
    onClick: (Int) -> Unit // 点击事件,参数为点击文字的下标
) 

Demo如下:

@Composable
fun TextDemo5(context: Context) {
    ClickableText(text = AnnotatedString("请点击我"), onClick = { index ->
        Toast.makeText(context, "点击位置:$index", Toast.LENGTH_SHORT).show()
    })
}

效果如下:

示例

如果要给整个Text()设置点击事件,直接使用Modifier.clickable{}即可。

3 给指定文字添加注解(超链接)

我们可以使用pushStringAnnotation()和pop()函数对来给指定文字添加注解,如下:

@Composable
fun TextDemo6(context: Context) {

    // 构建注解文本
    val url_tag = "article_url";
    val articleText = buildAnnotatedString {
        append("点击")

        // pushStringAnnotation()表示开始添加注解,可以理解为构造了一个<tag,annotation>的映射
        pushStringAnnotation(tag = url_tag, annotation = "https://devloper.android.com")
        // 要添加注解的文本为"展示Android官网"
        withStyle(style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold)) {
            append("展示Android官网")
        }
        // pop()表示注解结束
        pop()
    }

    // 构造可点击文本
    ClickableText(text = articleText, onClick = { index ->
        // 根据tag取出annotation并打印
        articleText.getStringAnnotations(tag = url_tag, start = index, end = index).firstOrNull()?.let { annotation ->
            Toast.makeText(context, "点击了:${annotation.item}", Toast.LENGTH_SHORT).show()
        }
    })
}

效果如下:

示例

Demo可在这里下载: gitee.com/lloydfinch/…

当然,Text的用法远不止此,更多的用法可以查看官方API即可。