Android开发 Jetpack_Compose_7 文字

发布时间 2023-10-13 11:54:37作者: 观心静

前言

  此篇博客主要讲解Compose里的文字相关的UI功能。文本处理相关的内容与细节较多,此篇博客尽量涵盖完整,所以博客较长需要耐心看完。

  官网文档:https://developer.android.google.cn/jetpack/compose/text?hl=zh-cn

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,              //最大行数
    minLines: Int = 1,                          //最小行数
    onTextLayout: (TextLayoutResult) -> Unit = {},//文本变化回调
    style: TextStyle = LocalTextStyle.current   //字体风格
){}

显示资源里的文字

Text(text = stringResource(id = R.string.hello))

文字的大小单位

有两种单位分别是sp与em。

sp是与屏幕密度有关换算后的数值单位,特点是不会跟随屏幕分辨率大小的改变而改变,能保持文字大小的一致性。一般情况下是常用这个单位。

em是相对字体大小的数值单位,1.em等于100%, 2.em则是200%相对大小,这时候在需要适配大小屏设备的应用上使用。这样在大屏上也能跟随改变文字的相对大小。

代码

@Composable
fun FontSizeDemo() {
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "以sp为单位的文字", fontSize = 20.sp)
            Text(text = "以em为单位的文字", fontSize = 2.em)
        }
    }
}

效果图

斜体

代码

Column(modifier = Modifier.align(Alignment.Center)){
    Text(text = "斜体", fontSize = 30.sp, fontStyle = FontStyle.Italic)
    Text(text = "正常", fontSize = 30.sp, fontStyle = FontStyle.Normal)
}

效果图

字体粗细

这可能要分辨率高的设备才能明显看出每阶字体粗细的区别

代码

Column(modifier = Modifier.align(Alignment.Center)){
    Text(text = "细", fontSize = 30.sp, fontWeight = FontWeight.Thin)
    Text(text = "较细", fontSize = 30.sp, fontWeight = FontWeight.ExtraLight)
    Text(text = "轻微细", fontSize = 30.sp, fontWeight = FontWeight.Light)
    Text(text = "正常", fontSize = 30.sp, fontWeight = FontWeight.Normal)
    Text(text = "中", fontSize = 30.sp, fontWeight = FontWeight.Medium)
    Text(text = "半粗", fontSize = 30.sp, fontWeight = FontWeight.SemiBold)
    Text(text = "粗", fontSize = 30.sp, fontWeight = FontWeight.Bold)
    Text(text = "大号粗体", fontSize = 30.sp, fontWeight = FontWeight.ExtraBold)
    Text(text = "黑体", fontSize = 30.sp, fontWeight = FontWeight.Black)
}

效果图

设置字体

首先需要搞个字体,这里推荐百度搜索阿里矢量图,在里面找到字体库(全是免费开源),选择一个下载,如下图:

在Android studio创建字体目录

选择font文件夹,然后ok创建

将下载完成的字体且后缀名是.ttf的文件放到font文件夹中

代码

系统会自带一些字体,我们可以直接使用,但是只支持英文

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Box(modifier = Modifier.fillMaxSize()) {
            Column(modifier = Modifier.align(Alignment.Center)) {
                Text(
                    text = "默认字体效果Default",
                    fontSize = 30.sp,
                    fontFamily = FontFamily.Default
                )
                //无衬线
                Text(
                    text = "无衬线SansSerif",
                    fontSize = 30.sp,
                    fontFamily = FontFamily.SansSerif
                )
                //衬线体,这个字体不支持中文,想看效果需要英文
                Text(text = "衬线体Serif", fontSize = 30.sp, fontFamily = FontFamily.Serif)
                //等宽字体,这个字体不支持中文,想看效果需要英文
                Text(
                    text = "等宽字体Monospace",
                    fontSize = 30.sp,
                    fontFamily = FontFamily.Monospace
                )
                //手写体Cursive,这个字体不支持中文,想看效果需要英文
                Text(text = "手写体Cursive", fontSize = 30.sp, fontFamily = FontFamily.Cursive)
                //添加我们下载的阿里的字体,此外这里的weight与style属性是用来设置字体粗细与斜体的,这个可能需要字体库本身支持这些设置
                Text(
                    text = "阿里方圆体字体效果",
                    fontSize = 30.sp,
                    fontFamily = FontFamily(
                        Font(
                            R.font.alimama_fang_yuan_tivf_thin,
                            weight = FontWeight.Normal,
                            style = FontStyle.Normal
                        )
                    )
                )
            }
        }
    }
}

效果图

字体间距

代码

Column(modifier = Modifier.align(Alignment.Center)) {
    Text(
        text = "文字间距测试1",
        fontSize = 30.sp,
        letterSpacing = 5.sp
    )
    Text(
        text = "文字间距测试2",
        fontSize = 30.sp,
        letterSpacing = 20.sp
    )
}

效果图

 

下划线与中划线

代码

Column(modifier = Modifier.align(Alignment.Center)) {
    Text(
        text = "下划线效果",
        fontSize = 30.sp,
        textDecoration = TextDecoration.Underline
    )
    Text(
        text = "中划线效果",
        fontSize = 30.sp,
        textDecoration = TextDecoration.LineThrough                    )
}

效果图

字体对齐方向

代码

Column(modifier = Modifier.align(Alignment.Center)) {
    Text(
        text = "左",
        color = Color.White,
        fontSize = 14.sp,
        textAlign = TextAlign.Start,
        //这里设置一个背景,让文字对齐方向有参照物
        modifier = Modifier.padding(top = 20.dp).size(100.dp).background(color = Color.DarkGray)
    )
    Text(
        text = "中",
        color = Color.White,
        fontSize = 14.sp,
        textAlign = TextAlign.Center,
        modifier = Modifier.padding(top = 20.dp).size(100.dp).background(color = Color.DarkGray)
    )
    Text(
        text = "右",
        color = Color.White,
        fontSize = 14.sp,
        textAlign = TextAlign.End,
        modifier = Modifier.padding(top = 20.dp).size(100.dp).background(color = Color.DarkGray)
    )
    //什么都没设置的效果,作为下面TextAlign.Justify左右对齐效果的参照物。
    Text(
        text = "Stretch lines of text that end with a soft line break to fill the width of the container.Lines that end with hard line breaks are aligned towards the Start edge.",
        color = Color.White,
        fontSize = 14.sp,
        modifier = Modifier.padding(top = 20.dp).size(160.dp).background(color = Color.DarkGray)
    )
    //设置后TextAlign.Justify 左右对齐后的效果。注意!这个属性对英文有有意义所以下面使用英文来演示,对中文没什么意义,因为中文文字宽度都一样,没有英文单词有长度上的变化。
    Text(
        text = "Stretch lines of text that end with a soft line break to fill the width of the container.Lines that end with hard line breaks are aligned towards the Start edge.",
        color = Color.White,
        fontSize = 14.sp,
        textAlign = TextAlign.Justify,
        modifier = Modifier.padding(top = 20.dp).size(160.dp).background(color = Color.DarkGray)
    )
}

效果图

行间距

代码

Column(modifier = Modifier.align(Alignment.Center)) {
    Text(
        text = "行间距测试一,ABCDEFGHWJKL",
        color = Color.White,
        fontSize = 20.sp,
        lineHeight = 10.sp,
        modifier = Modifier.padding(top = 20.dp).size(200.dp).background(color = Color.DarkGray)
    )
    Text(
        text = "行间距测试二,ABCDEFGHWJKL",
        color = Color.White,
        fontSize = 20.sp,
        lineHeight = 50.sp,
        modifier = Modifier.padding(top = 20.dp).size(200.dp).background(color = Color.DarkGray)
    )
}

效果图

文本内超出显示

代码

Column(modifier = Modifier.align(Alignment.Center)) {
    Text(
        text = "裁剪_Clip the overflowing text to fix its container.",
        color = Color.White,
        fontSize = 20.sp,
        overflow = TextOverflow.Clip,
        modifier = Modifier.padding(top = 20.dp).size(80.dp).background(color = Color.DarkGray)
    )
    Text(
        text = "省略_Use an ellipsis to indicate that the text has overflowed.",
        color = Color.White,
        fontSize = 20.sp,
        overflow = TextOverflow.Ellipsis,
        modifier = Modifier.padding(top = 20.dp).size(80.dp).background(color = Color.DarkGray)
    )
}

效果图

文本内容、参数、布局回调

每次文本变化后都会回调

代码

@Composable
fun TextLayout() {
    val count = remember { mutableStateOf(0) }
    val log = remember { mutableStateOf("") }
    //创建一个计数功能
    LaunchedEffect(true){
        while (isActive){
            count.value++
            delay(1000)
        }
    }
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(
                text = count.value.toString(),
                color = Color.Black,
                fontSize = 38.sp,
                onTextLayout = {
                    log.value = "文本内容 = ${it.layoutInput.text} \n ${it.toString()}"
                }
            )
            //这里实现一个Text,用来显示上面Text的变化日志
            Text(text = log.value, modifier = Modifier.padding(top = 30.dp))
        }
    }
}

效果图

文字组合拼接

这里的text = buildAnnotatedString,是另一个重载函数Text的参数(text: AnnotatedString),用于实现文本的拼接与单独的风格设置。这里会涉及到style的内容,这个会在下面讲解,这里只是先演示文字拼接的代码与效果。

代码

@Composable
fun BuildAnnotatedStringDemo() {
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(fontSize = 20.sp, text = buildAnnotatedString {
                append("拼接文字效果:\n")
                withStyle(style = SpanStyle(color = Color.Red, fontSize = 20.sp)) {
                    append("红")
                }
                withStyle(style = SpanStyle(color = Color.Yellow, fontSize = 30.sp)) {
                    append("黄")
                }
                withStyle(style = SpanStyle(color = Color.Blue, fontSize = 35.sp)) {
                    append("蓝")
                }
                append("\n----------分割线----------")
                //还能以下面嵌套的方式,进行文字的拼接组合
                withStyle(style = ParagraphStyle(lineHeight = 80.sp)) {
                    withStyle(style = SpanStyle(fontSize = 45.sp)) {
                        withStyle(style = SpanStyle(color = Color.Cyan)) {
                            append("青\n")
                        }
                        withStyle(style = SpanStyle(color = Color.Gray)) {
                            append("灰\n")
                        }
                        withStyle(style = SpanStyle(color = Color.Black)) {
                            append("黑\n")
                        }
                    }

                }
            })
        }
    }
}

效果图

文字组合拼接后独立点击

有时候会需求一行文字需要2个点击效果的情况,例如 <用户协议>与<隐私政策> 这种情况,点击用户协议与隐私政策分别需要2个不一样的点击回调。这里就可以使用文字组合拼接的独立点击功能

代码


@Composable
fun BuildAnnotatedStringDemo() {
    val context = LocalContext.current
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            val annotatedText = buildAnnotatedString {
                pushStringAnnotation(tag = "userAgreement", annotation = "<用户协议>")
                withStyle(
                    style = SpanStyle(
                        fontSize = 20.sp,
                        color = Color.Blue,
                        fontWeight = FontWeight.Bold
                    )
                ) {
                    append("<用户协议>")
                }
                /*
                 * 这里的pop函数很重要,在每完成一段字符串注解后,都要使用pop进行记录,防止和下面新增的其他字符串注解混在一起
                 * 如果不设置就会出现下面的点击一次,就会返回2个字符串注解
                 */
                pop()
                append("与")
                pushStringAnnotation(tag = "privacyPolicy", annotation = "<隐私政策>")
                withStyle(
                    style = SpanStyle(
                        fontSize = 20.sp,
                        color = Color.Blue,
                        fontWeight = FontWeight.Bold
                    )
                ) {
                    append("<隐私政策>")
                }
                pop()
            }
            ClickableText(
                text = annotatedText,
                onClick = { offset ->
                    Log.e("zh", "offset = ${offset}: ")
                    annotatedText.getStringAnnotations(
                        tag = "userAgreement",
                        start = offset,
                        end = offset
                    ).firstOrNull()?.let { annotation ->
                        Toast.makeText(context, "点击 ${annotation.item}", Toast.LENGTH_SHORT)
                            .show()
                    }
                    annotatedText.getStringAnnotations(
                        tag = "privacyPolicy",
                        start = offset,
                        end = offset
                    ).firstOrNull()?.let { annotation ->
                        Toast.makeText(context, "点击 ${annotation.item}", Toast.LENGTH_SHORT)
                            .show()
                    }
                }
            )
        }
    }
}

效果图

style风格_作为通用文字风格的例子

这是一个相当重要的属性,它可以组合上面的全部文字配置,下面的例子创建了一个style作为通用的文字风格,可以添加到任意Text上作为通用风格,这样可以大大减少我们配置文字属性的冗余重复的代码。

代码

@Composable
fun TextStyleDemo() {
    val style = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Blue,
            fontSize = 30.sp,
            lineHeight = 10.sp,
            fontWeight = FontWeight.Bold,
            fontStyle = FontStyle.Normal,
            textAlign = TextAlign.Center,
            background = Color.Gray,
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "文本1", style = style)
            Text(text = "文本2", style = style)
        }
    }
}

效果图

style风格_文字背景

上面的例子展示的都是上面已经说明过的属性,但是它可以完成更多的文字风格定制,从这里开始会讲解style特定的文字属性。

style风格的背景设置非常不一样,它并不是整个Text的背景颜色设置,而是根据文字占位的背景颜色

代码

@Composable
fun TextStyleDemo() {
    val style = LocalTextStyle.current.merge(
        TextStyle(
            background = Color.Gray //注意,这里的背景与下面的Text的背景是不同的
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", style = style, modifier = Modifier.size(100.dp).background(color = Color.Cyan))
        }
    }
}

效果图

 

style风格_设置文字在行距的位置

一共有4种样式,FirstLineTop、LastLineBottom、Both、None

代码

@Composable
fun TextStyleDemo() {
    val noneStyle = LocalTextStyle.current.merge(
        TextStyle(
            //这里增加行距,让lineHeightStyle的属性效果更明显
            lineHeight = 3.em,
            /*
                platformStyle这个属性,根据官网文档说是为了给第一行文字顶部和最后一行文字底部的字体指标添加额外的内边距。
                这个属性会影响下面的lineHeightStyle行距调整,所以需要设置为false关闭。
             */
            platformStyle = PlatformTextStyle(
                includeFontPadding = false
            ),
            lineHeightStyle = LineHeightStyle(
                alignment = LineHeightStyle.Alignment.Center,
                trim = LineHeightStyle.Trim.None
            ),
            //加个背景,可以看的更清楚行距的区别
            background = Color.Gray
        )
    )
    val bothStyle = LocalTextStyle.current.merge(
        TextStyle(lineHeight = 3.em, platformStyle = PlatformTextStyle(includeFontPadding = false),
            lineHeightStyle = LineHeightStyle(alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.Both),
            background = Color.Gray
        )
    )
    val lastLineBottomStyle = LocalTextStyle.current.merge(
        TextStyle(lineHeight = 3.em, platformStyle = PlatformTextStyle(includeFontPadding = false),
            lineHeightStyle = LineHeightStyle(alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.LastLineBottom),
            background = Color.Gray
        )
    )
    val firstLineTopStyle = LocalTextStyle.current.merge(
        TextStyle(lineHeight = 3.em, platformStyle = PlatformTextStyle(includeFontPadding = false),
            lineHeightStyle = LineHeightStyle(alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.FirstLineTop),
            background = Color.Gray
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "文字在行距中间\nLineHeightStyle.Trim.None\nLineHeightStyle.Trim.None", style = noneStyle)
            Text(text = "第一行在行距上面,最后一行在行距下面,中间行在中间\nLineHeightStyle.Trim.Both\nLineHeightStyle.Trim.Both", style = bothStyle, modifier = Modifier.padding(top = 20.dp))
            Text(text = "文字在行距下面\nLineHeightStyle.Trim.LastLineBottom\nLineHeightStyle.Trim.LastLineBottom", style = lastLineBottomStyle, modifier = Modifier.padding(top = 20.dp))
            Text(text = "第一行在行距上面,其他行在中间\nLineHeightStyle.Trim.FirstLineTop\nLineHeightStyle.Trim.FirstLineTop", style = firstLineTopStyle, modifier = Modifier.padding(top = 20.dp))
        }
    }
}

效果图

style风格_文字阴影

代码

@Composable
fun TextStyleDemo() {
    val shadowStyle1 = LocalTextStyle.current.merge(
        TextStyle(
            //offset是阴影的范围,范围越大阴影越大
            //blurRadius是阴影的模糊半径,数值越大阴影越模糊,越小而越清晰
            shadow = Shadow(color = Color.Blue, offset = Offset(2f, 2f), blurRadius = 1f)
        )
    )
    val shadowStyle2 = LocalTextStyle.current.merge(
        TextStyle(
            shadow = Shadow(color = Color.Blue, offset = Offset(5f, 5.0f), blurRadius = 10f)
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "文字阴影", fontSize = 20.sp, style = shadowStyle1)
            Text(text = "文字阴影", fontSize = 20.sp, style = shadowStyle2)
        }
    }
}

效果图

选中文字

 

TextField 输入框

 

 

 

End