观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17023101.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

  此篇博客主要讲解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")
                        }
                    }

                }
            })
        }
    }
}

效果图

文字组合拼接后独立点击

这里需要使用ClickableText这个独立组件,这个本不属于Text的一部分,但是为了连贯性,这里先行演示。

有时候会需求一行文字需要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风格_文字渐变色

代码

@OptIn(ExperimentalTextApi::class)
@Composable
fun TextStyleDemo() {
    val drawStyle = LocalTextStyle.current.merge(
        TextStyle(
            fontSize = 50.sp,
            brush = Brush.horizontalGradient(
                listOf(
                    Color.Magenta,
                    Color.Cyan,
                    Color.Blue,
                )
            )
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "渐变色", style = drawStyle)
        }
    }
}

效果图

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)
        }
    }
}

效果图

style风格_文字轮廓(描边)

简单的实线、虚线、路径圆角轮廓

代码

@OptIn(ExperimentalTextApi::class)
@Composable
fun TextStyleDemo() {
    //实线轮廓风格
    val outlineStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 80.sp,
            //width是描边的宽度,它决定了描边的线条粗细
            //join 自定义View的老知识了,决定笔画收尾的笔锋是圆角还是矩形,也可以不写,在文字描边里什么用处
            drawStyle = Stroke(width = 1f, join = StrokeJoin.Round)
        )
    )
    //虚线轮廓风格
    val dottedLineOutlineStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 80.sp,
            drawStyle = Stroke(
                width = 1f, pathEffect = PathEffect.dashPathEffect(
                    //intervals为虚线间隔参数,数组的第一个值为实段的长度,第二个值是空段的长度
                    intervals = floatArrayOf(10f, 5f),
                    //phase 为虚线的起始偏移值,一般情况下不死扣细节根本用不到这参数
                    phase = 0f
                ))
        )
    )
    //路径圆角轮廓风格
    val roundedOutlineStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 80.sp,
            drawStyle = Stroke(
                width = 1f,
                //cornerPathEffect是实现圆角路径的关键,这里的radius是这个圆角路径的半径
                pathEffect = PathEffect.cornerPathEffect(radius = 5f))
        )
    )

    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "实线描边", style = outlineStyle)
            Text(text = "虚线描边", style = dottedLineOutlineStyle)
            Text(text = "圆角描边", style = roundedOutlineStyle)
        }
    }
}

效果图

组合多个效果实现轮廓(描边)

代码

@OptIn(ExperimentalTextApi::class)
@Composable
fun TextStyleDemo() {
    val dottedLineOutlineStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 80.sp,
            drawStyle = Stroke(
                width = 1f, pathEffect =
                PathEffect.chainPathEffect(
                    PathEffect.dashPathEffect(intervals = floatArrayOf(10f, 2f), phase = 0f),
                    PathEffect.cornerPathEffect(radius = 10f)
                )
        ))
    )

    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "虚线加圆角", style = dottedLineOutlineStyle)
        }
    }
}

效果图

自定义形状的虚线轮廓

代码

@OptIn(ExperimentalTextApi::class)
@Composable
fun TextStyleDemo() {
    val dottedLineOutlineStyle1 = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 150.sp,
            drawStyle = Stroke(
                width = 2f, pathEffect = PathEffect.stampedPathEffect(shape = Path().apply {
                    //这里通过path自定义每一个虚线中的实线形状,这边画了一个圆形所以每一个实线都是圆形的
                    moveTo(0f, 0f)
                    addOval(Rect(0f, 0f, 5f, 5f))
                }, advance = 10f, phase = 10f, style = StampedPathEffectStyle.Translate)
                //上面的参数advance 是虚线的间距
            )
        )
    )

    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "自定义圆点虚线", style = dottedLineOutlineStyle1)
        }
    }
}

效果图

style风格_文字基线

文字在每一行的显示其实都有高度基线,这里可以通过baselineShift设置文字的行显示的基线高度,分别是上中下

代码

@Composable
fun TextStyleDemo() {
    //上基线
    val superscriptStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 14.sp,
            baselineShift = BaselineShift.Superscript,
            background = Color.Gray //加个背景比较好看清楚
        )
    )
    //None没有就算默认,默认中基线
    val noneStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 14.sp,
            baselineShift = BaselineShift.None,
            background = Color.Gray
        )
    )
    //下基线
    val subscriptStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 14.sp,
            baselineShift = BaselineShift.Subscript,
            background = Color.Gray
        )
    )
    //自定义基线高度
    val customBaselineStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 14.sp,
            baselineShift = BaselineShift(0.2f),
            background = Color.Gray
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "文字显示在上基线", style = superscriptStyle)
            Text(text = "文字显示在中基线", style = noneStyle,  modifier = Modifier.padding(top = 20.dp))
            Text(text = "文字显示在下基线", style = subscriptStyle,  modifier = Modifier.padding(top = 20.dp))
            Text(text = "自定义基线高度", style = customBaselineStyle,  modifier = Modifier.padding(top = 20.dp))
        }
    }
}

效果图

style风格_设置文字几何角度(设置文字的x轴缩放与倾斜)

代码

@Composable
fun TextStyleDemo() {
    val style = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 20.sp,
            //scaleX 为缩放文字X轴的大小
            //skewX 设置文字的左右倾斜度 如果是负值则向右倾斜, 如果是正值则向左倾斜
            textGeometricTransform = TextGeometricTransform(scaleX = 2.0f, skewX = 0.5f)
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "设置文字的x轴缩放与倾斜", style = style)
        }
    }
}

效果图

style风格_文字高级排版

一般情况开发用不上,做文档排版使用的,这里只简单演示代码

@Composable
fun TextStyleDemo() {
    val superscriptStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 20.sp,
            //由font提供的高级排版设置。格式与CSS的font-feature-settings属性相同
            //属性参考地址:https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop
            fontFeatureSettings = "smcp"
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "fontFeatureSettings", style = superscriptStyle)
        }
    }
}

style风格_设置段落方向

代码

@Preview
@Composable
fun TextStyleDemo() {
    //左
    val ltrDirectionStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 20.sp,
            textDirection = TextDirection.Ltr
        )
    )
    //右
    val rtlDirectionStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 20.sp,
            textDirection = TextDirection.Rtl
        )
    )

    val contentDirectionStyle = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Black,
            fontSize = 20.sp,
            /*
                TextDirection.Content该值表示根据Unicode双向算法,文本方向取决于文本中的第一个强方向字符。如果不存在强方向字符,
                则使用androidx.compose.ui.unit.LayoutDirection来解析最终的TextDirection。
             */
            textDirection = TextDirection.Content
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "段落右\n段落向右\n这行的段落向右结束", style = ltrDirectionStyle, modifier = Modifier.padding(top = 20.dp))
            Text(text = "段落左\n段落向左\n这行的段落向左结束", style = rtlDirectionStyle, modifier = Modifier.padding(top = 20.dp))
            Text(text = "段落系统判断\n这行的段落方向由系统判断", style = contentDirectionStyle, modifier = Modifier.padding(top = 20.dp))
        }
    }
}

效果图

style风格_设置换行方式

代码

@Preview
@Composable
fun TextStyleDemo() {
    val simple = LocalTextStyle.current.merge(
        TextStyle(
            /*
               LineBreak.Simple 快速的断行算法。非常适合经常更新的文本,如文本编辑器,因为文本将回流最小。
             */
            lineBreak = LineBreak.Simple,
        )
    )
    val heading = LocalTextStyle.current.merge(
        TextStyle(
            /*
              LineBreak.Heading 平衡行长度、连字符和基于短语的断行。适用于短文本,如标题或狭窄的报纸专栏。
             */
            lineBreak = LineBreak.Heading,
        )
    )
    val paragraph = LocalTextStyle.current.merge(
        TextStyle(
            /*
                LineBreak.Paragraph 更慢、更高质量的断行,以提高可读性。适用于大量的文本。

             */
            lineBreak = LineBreak.Paragraph,
        )
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.align(Alignment.Center)) {
            Text(text = "快速的断行算法。非常适合经常更新的文本,如文本编辑器,因为文本将回流最小。", style = simple, modifier = Modifier.padding(top = 20.dp).size(100.dp))
            Text(text = "平衡行长度、连字符和基于短语的断行。适用于短文本,如标题或狭窄的报纸专栏。", style = heading, modifier = Modifier.padding(top = 20.dp).size(100.dp))
            Text(text = "更慢、更高质量的断行,以提高可读性。适用于大量的文本。", style = paragraph, modifier = Modifier.padding(top = 20.dp).size(100.dp))
        }
    }
}

效果图

style风格_富文本(文字中添加图标)

代码

@Preview
@Composable
fun TextStyleDemo() {
    val text = buildAnnotatedString {
        append("请点击搜索")
        appendInlineContent(id = "SearchIcon")
    }
    val inlineContent = mapOf(
        "SearchIcon" to InlineTextContent(
            Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)
        ) { link ->
            Image(
                imageVector = Icons.Default.Search,
                modifier = Modifier.fillMaxSize(),
                contentDescription = ""
            )
        },
    )
    Box(modifier = Modifier.fillMaxSize()) {
        Text(text = text, inlineContent = inlineContent, modifier = Modifier.align(Alignment.Center))
    }
}

效果图

选中文字

代码

@Preview
@Composable
fun SelectionContainerDemo() {
    Box(modifier = Modifier.fillMaxSize()) {
        SelectionContainer(modifier = Modifier.align(Alignment.Center)){
            Column() {
                Text(text = "选中这段文字1")
                Text(text = "选中这段文字2")
                Text(text = "选中这段文字3")
            }
        }
    }
}

效果图

 

获取点击文字的位置

使用ClickableText 可以实现获取文本点击的位置信息,这个组件在上面的文字拼接中已经有了一个例子。这里只是再次演示一下正常使用

代码

@Preview
@Composable
fun SelectionContainerDemo() {
    val clickPosition = remember {
        mutableStateOf(0)
    }
    Box(modifier = Modifier.fillMaxSize()) {
        Column(modifier = Modifier.width(200.dp).fillMaxHeight().align(Alignment.Center)) {
            Text(
                text = "当前点击位置 = ${clickPosition.value}",
                fontSize = 20.sp,
                color = Color.Cyan,
                modifier = Modifier.padding(top = 100.dp)
            )
            ClickableText(text = AnnotatedString("豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。"), onClick = { offset ->
                clickPosition.value = offset
            })
        }
    }
}

效果图

TextField输入框

简单的例子

代码

@Preview
@Composable
fun TextFieldDemo() {
    //这里需要实现一个mutableStateOf提供给TextField
    //如果不实现TextField会出现无法输入内容的情况
    val inputText = remember {
        mutableStateOf("")
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 300.dp)
    ) {
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
        )
    }
}

效果图

全部参数

@Composable
fun TextField(
    value: String,                                                          //默认内容
    onValueChange: (String) -> Unit,                                        //内容变化回调
    modifier: Modifier = Modifier,                                          //修饰符
    enabled: Boolean = true,                                                //是否启用
    readOnly: Boolean = false,                                              //是否是只读模式
    textStyle: TextStyle = LocalTextStyle.current,                          //文字风格
    label: @Composable (() -> Unit)? = null,                                //标题
    placeholder: @Composable (() -> Unit)? = null,                          //当输入框没有内容时,显示的占位内容
    leadingIcon: @Composable (() -> Unit)? = null,                          //前面的图标
    trailingIcon: @Composable (() -> Unit)? = null,                         //后面的图标
    isError: Boolean = false,                                               //指示文本字段的当前值是否出错
    visualTransformation: VisualTransformation = VisualTransformation.None, //指定输入类型
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,             //按键选择
    keyboardActions: KeyboardActions = KeyboardActions(),                   //按键操作
    singleLine: Boolean = false,                                            //是否单行
    maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,                   //最大行数
    minLines: Int = 1,                                                      //最小行数
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },  //交互效果(选中效果、按下效果等等)
    shape: Shape =                                                          //形状
        MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
    colors: TextFieldColors = TextFieldDefaults.textFieldColors()           //颜色
) {
}

启用与禁用

设置启用状态,会让输入框彻底禁用全部交互操作。

效果图

代码

@Preview
@Composable
fun TextFieldDemo() {
    val isEnabledTextField = remember {
        mutableStateOf(false)
    }
    val inputText = remember {
        mutableStateOf("")
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 200.dp)
    ) {
        IconToggleButton(checked = isEnabledTextField.value, onCheckedChange = {
            isEnabledTextField.value = it
        }, modifier = Modifier.width(200.dp)) {
            Text(text = if (isEnabledTextField.value) "设置禁用" else "设置启用")
        }
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
            enabled = isEnabledTextField.value
        )
    }
}

设置只读状态

与上面的启用状态不同的是,只读状态只禁用输入,但是依然可以复制内容中的文本。

效果图

代码

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun TextFieldDemo() {
    val isReadOnly = remember {
        mutableStateOf(false)
    }
    val inputText = remember {
        mutableStateOf("")
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 200.dp)
    ) {
        IconToggleButton(checked = isReadOnly.value, onCheckedChange = {
            isReadOnly.value = it
        }, modifier = Modifier.width(200.dp)) {
            Text(text = if (isReadOnly.value) "取消只读" else "设置只读")
        }
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
            readOnly = isReadOnly.value
        )
    }
}

预设内容

这里是预设内容是指下面的4个参数。这4个分别对应着不同的位置,并且都是composable,所以内容实现非常自由,可以完全根据你自己想法实现各种效果。

label: @Composable (() -> Unit)? = null,                                //标题
placeholder: @Composable (() -> Unit)? = null,                          //当输入框没有内容时,显示的占位内容
leadingIcon: @Composable (() -> Unit)? = null,                          //前面的图标
trailingIcon: @Composable (() -> Unit)? = null,                         //后面的图标

效果图

代码

@Preview
@Composable
fun TextFieldDemo() {
    val inputText = remember {
        mutableStateOf("")
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 50.dp)
    ) {
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
            label = {
                Text(text = "label 标签")
            },
        )
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
            placeholder = {
                Text(text = "placeholder 占位符")
            },
        )
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
            leadingIcon = {
                //前图标
                Icon(imageVector = Icons.Filled.Build, null)
            },
        )
        TextField(
            value = inputText.value,
            onValueChange = { inputText.value = it },
            trailingIcon = {
                //后图标
                Icon(imageVector = Icons.Filled.Search, null)
            }
        )
    }
}

错误状态

下面的例子中,通过判断是否输入999这个内容而改变输入框的错误状态

效果图

代码

@Preview
@Composable
fun TextFieldDemo() {
    val inputText = remember {
        mutableStateOf("")
    }
    val error = remember {
        mutableStateOf(false)
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 50.dp)
    ) {
        TextField(
            value = inputText.value,
            onValueChange = {
                error.value = it == "999"
                inputText.value = it
            },
            isError = error.value
        )
    }
}

隐藏或者显示内容

下面的例子就是实现了显示与隐藏密码的功能,其中关键部分是visualTransformation

效果图

代码

@Preview
@Composable
fun TextFieldDemo() {
    val inputText = remember {
        mutableStateOf("")
    }
    val isShowPwd = remember {
        mutableStateOf(false)
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 50.dp)
    ) {
        TextField(
            value = inputText.value,
            onValueChange = {
                inputText.value = it
            },
            trailingIcon = {
                val show = painterResource(id = R.drawable.baseline_visibility_24)
                val hide = painterResource(id = R.drawable.baseline_visibility_off_24)
                Icon(
                    painter = if (isShowPwd.value) show else hide,
                    contentDescription = null,
                    modifier = Modifier.clickable {
                        isShowPwd.value = !isShowPwd.value
                    })
            },
            visualTransformation = if (isShowPwd.value) VisualTransformation.None else PasswordVisualTransformation()
        )
    }
}

键盘类型

可以通过设置keyboardOptions改变弹出输入键盘的类型,并且一共有以下几种

  • KeyboardType.Text  能够输入常规的键盘类型
  • KeyboardType.Ascii  能够输入ASCII字符键盘类型
  • KeyboardType.Number  能够输入数字的键盘类型
  • KeyboardType.Phone  能够输入电话号码的键盘类型
  • KeyboardType.Uri  能够输入uri的键盘类型
  • KeyboardType.Email  能够输入电子邮件地址的键盘类型
  • KeyboardType.Password  能够输入密码的键盘类型
  • KeyboardType.NumberPassword  能够输入数字密码的键盘类型
  • KeyboardType.Decimal  能够输入小数的键盘类型

代码

@Preview
@Composable
fun TextFieldDemo() {
    val inputText = remember {
        mutableStateOf("")
    }
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 50.dp)
    ) {
        TextField(
            value = inputText.value,
            onValueChange = {
                inputText.value = it
            },
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
        )
    }
}

 

End

posted on 2023-10-13 11:49  观心静  阅读(439)  评论(0编辑  收藏  举报