前言
在app开发中,很多场景需要在有限的范围内显示完整的内容。如果内容本身没有超出控件范围,那么没有任何问题。但是如果内容的长度超过了控件的范围,那么就需要酌情处理了。而跑马灯效果就是处理方式之一。
跑马灯并不是什么新东西,在xml的时代,textview就可以通过简单的属性设置实现效果。不知道用过的xdm有没有跟我同样的感受,就是往往很难一下子完美的实现效果,因为什么焦点啊,行数啊这些属性都会影响到最终的效果并且有些场景需要多个textview按需跑马灯等等这些问题,总之让这个简单的功能实现起来不是那么顺畅~
当然啊,好歹xml的方式还是可以实现的。而强大的Compose居然在1.4.0-alpha04版本之前不支持跑马灯效果!这几天看文档发现从1.4.0-alpha04已经支持了跑马灯这个可能有点迟来的功能。下面就来一起探索一下Compose的跑马灯实现吧。
最快实现
实现非常简单,调用Modifier.basicMarquee()
即可!不用像之前xml的时候,需要设置焦点行数等。
Text(
modifier = Modifier.padding(padding).basicMarquee(),
text = "套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!",
)
首页效果是用到了大话Compose炼体(1)-先一餐吃3碗饭 - 掘金 (juejin.cn)这篇的实现。感兴趣的可以去看看点个赞,这里主要看Text
的跑马灯效果就好
另外我们缩减Text的text内容,再来看看
Text(
modifier = Modifier.padding(padding).basicMarquee(),
text = "套马杆的汉子你威武雄壮!",
)
因为这里的Text的宽没有限制啊,所以是屏幕宽度。那么这里的控件宽度肯定大于内容,发现这是是没有跑马灯效果的
如果我们再把Text的宽度指定到一个肖小于内容的值示例为50dp
Text(
modifier = Modifier.padding(padding).width(50.dp).basicMarquee(),
text = "套马杆的汉子你威武雄壮!",
)
发现跑马灯效果又出现了。这里就不再贴图演示了。
结论:只有内容超出了有限范围才会有跑马灯效果,反之没有!
配合焦点实现跑马灯
考虑到电量优化和性能优化,很多时候让跑马灯无条件并无限时长的运行并不是个好主意。上面提到要实现跑马灯我们要调用Modifier.basicMarquee
方法。这个方法都有默认参数,所以我们上面没有传参就能实现了。稍微看了一下参数类型和默认值,发现animationMode
这个字段可以控制跑马灯动画的触发条件
fun Modifier.basicMarquee(
iterations: Int = DefaultMarqueeIterations,
//关键参数
animationMode: MarqueeAnimationMode = Immediately,
delayMillis: Int = DefaultMarqueeDelayMillis,
initialDelayMillis: Int = if (animationMode == Immediately) delayMillis else 0,
spacing: MarqueeSpacing = DefaultMarqueeSpacing,
velocity: Dp = DefaultMarqueeVelocity
): Modifier
//动画模式
value class MarqueeAnimationMode private constructor(private val value: Int) {
override fun toString(): String = when (this) {
Immediately -> "Immediately"
WhileFocused -> "WhileFocused"
else -> error("invalid value: $value")
}
companion object {
/**
*不管有没焦点状态,立即执行
*/
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@ExperimentalFoundationApi
@get:ExperimentalFoundationApi
val Immediately = MarqueeAnimationMode(0)
/**
* 只有控件有焦点或者子控件有焦点时才执行
*/
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@ExperimentalFoundationApi
@get:ExperimentalFoundationApi
val WhileFocused = MarqueeAnimationMode(1)
}
}
看到这心里就有个实现想法了,通过点击来控制焦点,然后通过是否有焦点来控制跑马灯动画是否运行。对Compose焦点管理不了解的xdm可以花个几分钟看看这篇文章Jetpack Compose中的焦点管理 - 掘金 (juejin.cn)。
改造代码如下:
//获取并记住焦点申请器,后面用它来申请焦点
val focusRequester = remember { FocusRequester() }
//获取焦点管理器 后面用它来清理焦点
val focusManager = LocalFocusManager.current
//申明一个变量来记录当前是否有焦点
var isFocused = false
Text(
text = "套马杆的汉子你威武雄壮!套马杆的汉子你威武雄壮!",
Modifier
.padding(padding)
.width(200.dp)
//传参 MarqueeAnimationMode.WhileFocused用焦点来控制跑马灯动画
.basicMarquee(animationMode = MarqueeAnimationMode.WhileFocused)
//设置焦点申请器
.focusRequester(focusRequester)
//设置焦点改变监听
.onFocusChanged {
isFocused = it.isFocused
}
//这句必须要有,调用后表明可以被聚焦
.focusable()
//设置点击事件
.clickable {
if (isFocused) {
//如果当前有焦点,也就是在跑动画时。清楚焦点,停止动画。
focusManager.clearFocus()
} else {
//如果当前没有焦点,申请焦点,开始动画
focusRequester.requestFocus()
}
},
)
可以看到效果是符合预期的
结论:可以通过焦点来控制动画的起止。失去焦点停止动画,并回到初始的状态而不是动画停止时的状态。
都能跑 才是好的跑马灯
之前大多数场景用到跑马灯,都是Text这种控件。但是Compose的跑马灯是否只能用在Text上呢?答案是“并不是哟”
。所有可组合项都可以用到跑马灯效果!这个就是发挥xdm脑洞的时候了,下面展示一个简单效果:
调快一点速度,默认是30dp,这里改成了60;减少一点动画头尾的距离,默认是3/1屏幕宽,这里改成10/1
Row(modifier = Modifier
.padding(padding)
.basicMarquee(velocity =60.dp, spacing = MarqueeSpacing.fractionOfContainer(1f / 10f))) {
Image(painter = painterResource(id = R.drawable.avatar1) , contentDescription = null)
Image(painter = painterResource(id = R.drawable.avatar2) , contentDescription = null)
Image(painter = painterResource(id = R.drawable.avatar3) , contentDescription = null)
Image(painter = painterResource(id = R.drawable.avatar4) , contentDescription = null)
Image(painter = painterResource(id = R.drawable.avatar5) , contentDescription = null)
Image(painter = painterResource(id = R.drawable.avatar6) , contentDescription = null)
}
小结
通过上面的内容,可以发现。在Compose中要实现跑马灯效果非常简单,核心方法就是basicMarquee()
,并且所有可组合项都可以通过Modifier来实现。具体有哪些其他的地方可以用到,这就看大家的想象力了!欢迎留言说说你的想法!