小小段间距,竟也费神费力

段间距理论上讲是一个很常见的需求,但是无论是 View 系统的 TextView, 亦或是 Compose 系统的 Text,都没有给出一个段间距的设置,也是比较神奇。

使用 Text 组件,如果你想要段间距的效果,那么网上推荐的做法是通过换行符去分割,然后用多个 Text 来承载。

这么做一般情况下是没什么问题的,但是如果你要对 Text 做部分高亮、划线、改变颜色等,就需要用到 Span 这个玩意儿,它是用 Range 区间去控制范围的,如果用多个 Text 的话,那么 Range 的处理就显得很麻烦了。

所以得上黑科技。

那黑科技是什么呢?答案就是 AnnotatedString 可以添加 ParagraphStyle。 对于文本某个 Range 添加 ParagraphStyle,那么它就会表现得像加了换行符一样。

@Composable
fun MultiText(modifier: Modifier = Modifier, text: String, lineHeight: TextUnit, paragraphSpace: TextUnit){
    val annotationStr = remember(text) {
        buildAnnotatedString {
            append(text)
            var start = 0
            var index = text.indexOf("\n")
            while (index > 0 && index < text.length - 1) {
                addStyle(ParagraphStyle(
                    lineHeight = lineHeight,
                    lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None),
                    platformStyle = PlatformParagraphStyle(false)
                ), start, index)
                start = index + 1
                index = text.indexOf("\n", index + 2)
            }
            if (start < text.length) {
                addStyle(ParagraphStyle(
                    lineHeight = lineHeight,
                    lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None),
                    platformStyle = PlatformParagraphStyle(false)
                ), start, text.length)
            }
        }
    }
    Text(
        modifier = modifier,
        text = annotationStr,
        lineHeight = paragraphSpace, // Text 的 lineHeigh 实际用于段间距
        style = TextStyle(
            lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.None),
            platformStyle = PlatformTextStyle(false),
        )
    )
}

在上述代码中,我通过 \n 去分割,为每一段强制补上 ParagraphStyle, 如此一来,\n 就单独成一段了,因此我们就可以通过控制 \nlineHeight 来模拟端间距。

因为它游离于 ParagraphStyle 外,我们不能单独控制它的 lineHeight, 所以我们可以将整个 TextlineHeight 赋给 \n, 每个段落的 lineHeightParagraphStyle 来控制。

上面的实现可以工作,但是有一个问题,就是段间距要求至少大于字体的高度,如果期望段间距小点,那是不行的。

所以我们需要把 Text 的字号设的特别小,然后真正文字的字号放到每个段里面单独控制。但是因为 ParagraphStyle 无法控制字号,所以我们还得用上 SpanStyle

因此代码变更为:

@Composable
fun MultiText(modifier: Modifier = Modifier, text: String, lineHeight: TextUnit, paragraphSpace: TextUnit){
    val annotationStr = remember(text) {
        buildAnnotatedString {
            append(text)
            var start = 0
            var index = text.indexOf("\n")
            while (index > 0 && index < text.length - 1) {
                addStyle(ParagraphStyle(...), start, index)
                addStyle(SpanStyle(fontSize = 16.sp), start, index) // 用 SpanStyle 控制字号
                start = index + 1
                index = text.indexOf("\n", index + 2)
            }
            if (start < text.length) {
                addStyle(ParagraphStyle(...), start, text.length)
                addStyle(SpanStyle(fontSize = 16.sp), start, text.length)
            }
        }
    }
    Text(
        modifier = modifier,
        text = annotationStr,
        lineHeight = paragraphSpace,
        fontSize = 1.sp, // Text 本身的字号调整到特别小
        style = TextStyle(...)
    )
}

经过这一复杂的包装,我们就可以在业务上使用了。

MultiText(
    text = text,
    lineHeight = 16.sp,
    paragraphSpace = 10.sp,
    modifier = Modifier
        .padding(horizontal = 16.dp, vertical = 14.dp)
)

当然,实际使用场景,可能是是需要对 AnnotatedString 补加划线、高亮等各种功能,而不是抽取这么一个意义不大的组件。只是实现原理就是这样子。

也不知道国外是怎么考量的,这么个常用的需求,为啥就不直接支持下,非得让业务方写这么一大段复杂的代码。

如果是 View 系统想要实现段间距,Span 也能做到,不过不同系统会有坑,所以推荐还是用 QMUI 提供的 LineTypeView

我是古哥E下,前微信读书客户端程序猿 / 自学 5 年中医,维护过上万 Star 开源项目 QMUI Android,现独立维护好用简洁的 Android 组件库 emo

关注我可得:ChatGPT 开发玩法 | 程序员学习经验 | 组件库新变动 | 中医健康调理 。

emo官网:emo.qhplus.cn

全部评论

相关推荐

2025-12-31 14:31
湖南科技大学 Web前端
是阿亮吖:一个是这个时间招人比较少,另一个是沟通太少了。六十多份太养生了,最起码沟通个五六百份吧
点赞 评论 收藏
分享
老粉都知道小猪猪我很久没更新了,因为秋招非常非常不顺利,emo了三个月了,接下来说一下我的情况吧本人是双非本&nbsp;专业是完全不着计算机边的非科班,比较有优势的是有两段大厂实习,美团和字节。秋招面了50+场泡池子泡死的:滴滴&nbsp;快手&nbsp;去哪儿&nbsp;小鹏汽车&nbsp;不知名的一两个小厂其中字节13场&nbsp;两次3面挂&nbsp;两次2面挂&nbsp;一次一面挂其中有2场面试题没写出来,其他的都是全a,但该挂还是挂,第三次三面才面进去字节,秋招加暑期总共面了22次字节,在字节的面评可以出成书了快手面了8场,2次实习的,通过了但没去,一次2面挂&nbsp;最后一次到录用评估&nbsp;至今无消息滴滴三面完&nbsp;没几天挂了&nbsp;所有技术面找不出2个问题是我回答不上来的,三面还来说我去过字节,应该不会考虑滴滴吧,直接给我干傻了去哪儿一天速通&nbsp;至今无消息小鹏汽车hr&nbsp;至今无消息美团2面挂&nbsp;然后不捞我了,三个志愿全部结束,估计被卡学历了虾皮二面挂&nbsp;这个是我菜,面试官太牛逼了拼多多二面挂&nbsp;3道题也全写了&nbsp;也没问题是回答不出来的&nbsp;泡一周后挂腾讯面了5次&nbsp;一次2面挂&nbsp;三次一面挂,我宣布腾讯是世界上最难进的互联网公司然后还有一些零零散散的中小厂,但是数量比较少,约面大多数都是大厂。整体的战况非常惨烈,面试机会少,就算面过了也需要和各路神仙横向对比,很多次我都是那个被比下去的人,不过这也正常,毕竟谁会放着一个985的硕士不招,反而去招一个双非读化学的小子感觉现在互联网对学历的要求越来越高了,不仅仅要985还要硕士了,双非几乎没啥生存空间了,我感觉未来几年双非想要进大厂开发的难度应该直线上升了,唯一的打法还是从大二刷实习,然后苟个转正,不然要是去秋招大概率是炮灰。而且就我面字节这么多次,已经开始问很多ai的东西了,你一破本科生要是没实习没科研懂什么ai啊,纯纯白给了
不知名牛友_:爸爸
秋招你被哪家公司挂了?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务