ITmob-Ly
发布于 2022-12-30 / 468 阅读
0

Jetpack Compose 怎样实现跑马灯效果(Marquee)

Jetpack Compose Marquee

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

AndroidView marquee

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

Jetpack Compose marquee

现在是 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",
        ]
    }
    ...
}