《Jetpack Compose系列学习》-6 Compose的简单控件之Text

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" // 文字样式 加粗和斜体
    ...
    />
复制代码

显示效果如下图:

image.png

那么,Compose如何显示文字呢?其实我们前面的例子中都应该看的很清楚了,我们再来看一下:

@Composable
fun Greeting(name: String, isShowName: Boolean) {
    val showName = if (isShowName) "显示名字" else "不显示名字"
    Text(text = "Hello $name! $showName")
}
复制代码

image.png

对,就这么简单,这里我们使用硬编码的方式,但是我们的编码习惯都是将字符串资源定义到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看到预览效果:

image.png

如果我们不需要进行其他设置,直接使用上面的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
    )
}
复制代码

image.png
我们把文字设成了红色,可以看到预览效果。

字号大小

image.png
字号大小参数fontSize的类型是TextUnit,我们设置字号大小的时候使用了Int.sp的形式,其实这是Compose为我们写的扩展函数

@Stable
val Int.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
复制代码

Int、Float、Double都可以这样使用,即参数类型为TextUnit我们可以使用这种方式来设置。

斜体

我们将上面的Text修改为斜体:

image.png
我们通过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。我们来设置下看看效果。

image.png
这里说明下,FontWeight不只能设置自带的粗细,我们可以使用自定义的值,如:

image.png

字体

我们使用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种字体,我加上了注释,我们看看它们的区别,我们通过一个竖布局来整体看下:

image.png
当然我们可以按照Android那样,添加自定义的字体和字型,把对应的字体文件放到res/font目录下:

image.png

放置完成后,需要根据字体文件来定义fontFamily:

image.png

字符间距

我们要设置文本中字符中间的间距(空间量),使用的是Text的letterSpacing参数,可以看到这个参数类型和上面说的设置字号大小的一样,都是属于TextUnit,所以我们直接通过”.sp”的方式来设置即可。

image.png

文字装饰

通过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类似,都是在一个类中定义了伴生对象,里面定义了几种常用的格式,我们来通过代码设置看下效果:

image.png

文字对齐方式

使用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)
    }
}
复制代码

里面定义了几种对齐方式,我们这里只看看居中的对齐方式,别的方式大家可以自己试验下。

image.png

行高

行高通过lineHeight参数设置,它类型也是TextUnit,所以我们和前面设置字号大小、字间距的方式一样:

Text(
      text = "我是Compose爱好者",
      lineHeight = 80.sp
  )
复制代码

image.png

文字溢出

处理文字溢出情况,使用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)
    }
}
复制代码

image.png

最大行数就不说了,默认值是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。

image.png

设置文字选择

整体来看Text的使用是不是比Android View中的TextView简单些?我们再来看看怎么设置文字的选择,文字选择就是长按文字时弹出的复制、粘贴等按钮,确定文字的选择范围。

默认情况下,可组合项不可选择,即不支持长按复制选择,如果要选择的话,需要使用SelectionContainer可组合项封装文字元素:

image.png

我们可以看到长按文字支持选择复制等操作了。如果我们想文字有些字符不让用户去选择,那么怎么去做呢?我们只需要使用DisableSelection可组合项来封装不可选择的文字部分即可:

image.png

image.png

我们可以看出圈红色部分的文字我们设置用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)
    }
})
复制代码

image.png

我们能看到点击“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。后面我们会介绍学习其它的控件。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享