Marquee 效果也就是跑马灯效果,在使用传统的 XML 布局实现时,使用 TextView
可以很容易实现,当文字内容超过 View
的宽度时,设置不同的 ellipsize 会有不同的显示效果,设置 android:ellipsize="marquee"
等属性即可实现。使用 Jetpack Compose 怎样实现呢?
1. 使用 AndroidView 实现跑马灯效果
那使用 Jetpack Compose 怎样实现跑马灯效果呢,Jetpack Compose 的文档中并没有相关的介绍,Text
可组合函数的 overflow
属性只有 Clip
Ellipsis
Visible
三种可选。
这时我们首先想到的肯定是通过 AndroidView
在 Compose 中使用传统的 TextView
,这是可行的,上图中 Android marquees: 部分就是这样实现的。
使用 AndroidView 实现跑马灯效果:
@Preview(showBackground = true)
@Composable
fun BasicMarqueeDemo() {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = spacedBy(4.dp)
) {
Text("Android marquees:", style = MaterialTheme.typography.titleMedium)
AndroidMarqueeTextView("short", Modifier.fillMaxWidth())
listOf(40.dp, 80.dp, 120.dp).forEach {
AndroidMarqueeTextView("long text in short marquee", Modifier.width(it))
}
AndroidMarqueeWithClickableLink()
Row {
Text("Tap to focus: ")
AndroidMarqueeTextView(
"only animate when focused",
Modifier.size(80.dp, 30.dp),
initiallySelected = false,
focusable = true
)
}
}
}
@Composable
private fun AndroidMarqueeTextView(
text: CharSequence,
modifier: Modifier = Modifier,
initiallySelected: Boolean = true,
focusable: Boolean = false
) {
var isFocused by remember { mutableStateOf(false) }
AndroidView(
modifier = modifier
.border(if (isFocused) 4.dp else 1.dp, Color.Black)
.onFocusChanged { isFocused = it.hasFocus },
factory = {
TextView(it).apply {
ellipsize = TextUtils.TruncateAt.MARQUEE
marqueeRepeatLimit = -1 // repeat forever
// Enable clickable spans.
movementMethod = LinkMovementMethod.getInstance()
isSingleLine = true
isSelected = initiallySelected
isFocusableInTouchMode = focusable
}
},
update = {
it.text = text
}
)
}
2. 使用第三方库
也可以使用第三方库实现跑马灯效果,不过有更好的方法实现,这里就不多做介绍了。
3. 使用官方的实现方式
2022年12月14日,androidx 项目有新的提交:“Introduce marquee modifier.“
增加了一个 BasicMarquee.kt 通过 Modifier.basicMarquee 实现跑马灯效果,也就是说它不止可以用于文字,也可以用于其他可组合项,比如使用 basicMarquee 实现 Row
的跑马灯效果:
@Preview(showBackground = true)
@Composable
fun BasicMarqueeDemo() {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = spacedBy(4.dp)
) {
MarqueeRow()
Text("Compose marquees:", style = MaterialTheme.typography.titleMedium)
MarqueeText("short", Modifier.fillMaxWidth())
listOf(40.dp, 80.dp, 120.dp).forEach {
MarqueeText("long text in short marquee", Modifier.width(it))
}
MarqueeText(
"backwards animation",
Modifier.width(80.dp),
velocity = -DefaultMarqueeVelocity
)
MarqueeWithClickable()
Row {
Text("Tap to focus: ")
MarqueeText(
"only animate when focused",
Modifier.size(80.dp, 30.dp),
animationMode = WhileFocused
)
}
}
}
@Composable
fun MarqueeRow() {
val uriHandler = LocalUriHandler.current
Row(
Modifier
.fillMaxWidth()
.border(1.dp, Color.Black)
.basicMarquee(iterations = Int.MAX_VALUE),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier.size(40.dp),
imageVector = Icons.Default.Home,
contentDescription = ""
)
Text(
text = "Hello! ",
)
TextButton(onClick = { uriHandler.openUri("https://itmob.cn") }) {
Text("ITmob.cn")
}
Text(" Click the link to open the ITmob.cn website for more infomation.")
}
}
现在是 2022年12月29日,当前 compose fundation 的最新版本是 1.4.0-alpha03 还不包含这个提交,未来的版本应该会包含
basicMarquee
。
如果我们现在就想使用它,也可以暂时直接将 BasicMarquee 的源码用于我们的项目中,正式版本发布后在替换它。
问题:
直接将 BasicMarquee 源码放入自己的项目中使用可能会编译不通过,会出现如下错误:
Inheritance from an interface with '@JvmDefault' members is only allowed with -Xjvm-default option
出现 @JvmDefault
错误,可以修改 Gradle 中的 Kotlin 选项,增加 "-Xjvm-default=all-compatibility"
选项。
// build.gradle
android {
...
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += [
"-Xjvm-default=all-compatibility",
]
}
...
}