显示大段文字时有时并不需要默认显示全部内容,有时需要将大段文字折叠起来,当用户点击 “显示更多” 按钮时再展开显示全部内容,能给用户带来更好的用户体验。
本文将介绍使用 Jetpack Compose 时怎样实现文本折叠和展开,并通过 SizeAnimationModifier
修饰符让展开/折叠的动画更平滑。
下面介绍常见两种可展开文本的实现方式:
- 显示一个按钮给用户来操作展开和折叠操作
- 在文本的末尾显示可点击的文字:“Show More” ”Show Less”
通过按钮展开/折叠文本
如下定义了一个可组合项 ExpandableText
通过点击按钮切换展开或折叠文本:
@Composable
fun ExpandableText(
text: String,
modifier: Modifier = Modifier,
collapsedMaxLine: Int = 3,
) {
var isExpanded by remember { mutableStateOf(false) }
var expandable by remember { mutableStateOf(false) }
Column(
modifier = Modifier.then(modifier),
) {
Text(
text = text,
modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
maxLines = if (isExpanded) Int.MAX_VALUE else collapsedMaxLine,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textLayoutResult ->
if (!isExpanded && textLayoutResult.hasVisualOverflow) {
expandable = true
}
},
)
if (expandable) {
TextButton(
modifier = Modifier.align(Alignment.End),
onClick = { isExpanded = !isExpanded },
) {
Text(text = if (isExpanded) "Show Less" else "Show More")
}
}
}
}
上面的可组合项实现了如果文本少于等于 3 行,不显示 “显示更多“ ”显示较少“ 按钮。文本超过三行时显示 “显示更多“ ”显示较少“ 按钮,可以对大段文本进行折叠和展开。
要使用它,只需在需要的地方调用该可组合项即可:
ExpandableText(
text = "Thank you for taking the time to read this article from itmob.cn . As the author of this blog, I am excited to share my insights, experiences, and stories with all of you.",
)
运行效果如下:
通过点击文末文字展开/折叠文本
上面的实现是通过点击按钮切换文本折叠或展开的方式很简单,下面介绍实现在文本结尾添加可点击的文字实现展开文本。
实现的两个关键点:
- 文本折叠后移除几个字符留给 “Show More” 可点击文字
- 通过
AnnotatedString
实现文字的可点击效果
@Composable
fun ExpandableText(
text: String,
modifier: Modifier = Modifier,
collapsedMaxLine: Int = 3,
) {
var isExpanded by remember { mutableStateOf(false) }
var expandable by remember { mutableStateOf(false) }
var lastVisibleCharIndex by remember { mutableIntStateOf(0) }
val (showMoreText, showLessText) = " ... Show More" to "Show Less"
val toggleTextStyle = SpanStyle(
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary,
)
val annotatedText = buildAnnotatedString {
if (expandable) {
if (isExpanded) {
append(text)
pushStringAnnotation("TAG_TOGGLE", "https://itmob.cn")
withStyle(style = toggleTextStyle) { append(showLessText) }
pop()
} else {
val textWithToggleSpace = text.substring(startIndex = 0, endIndex = lastVisibleCharIndex)
.dropLast(showMoreText.length)
append(textWithToggleSpace)
pushStringAnnotation("TAG_TOGGLE", "https://itmob.cn")
withStyle(style = toggleTextStyle) { append(showMoreText) }
pop()
}
} else {
append(text)
}
}
Column(
modifier = Modifier.then(modifier),
) {
ClickableText(
modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
onClick = { offset ->
val annotations = annotatedText.getStringAnnotations("TAG_TOGGLE", offset, offset)
annotations.firstOrNull()?.let {
isExpanded = !isExpanded
}
},
text = annotatedText,
maxLines = if (isExpanded) Int.MAX_VALUE else collapsedMaxLine,
onTextLayout = { textLayoutResult ->
if (!isExpanded && textLayoutResult.hasVisualOverflow) {
expandable = true
lastVisibleCharIndex = textLayoutResult.getLineEnd(collapsedMaxLine - 1)
}
},
)
}
}
运行效果如下:
其他
更多关于 AnnotatedString
的介绍参见: