- 需求是图标控件宽度固定,控件内展示不全的话,内容会自动滚动到末尾,然后循环。
首先想到是TextView的marquee属性,如果TextView靓仔能包办的话那自然极好。具体操作的 话,TextView展示图片有drawableLeft、加载html、ImageSpan等方式,但都无法做到图片属性的完全自定义,况且TextView本身的跑马灯效果还有些小瑕疵,遂舍弃~
接着是学习TextView源码,企图在源码基本找到跑马灯逻辑
Marquee(TextView v) {
final float density = v.getContext().getResources().getDisplayMetrics().density;
mPixelsPerMs = MARQUEE_DP_PER_SECOND * density / 1000f;
mView = new WeakReference<TextView>(v);
mChoreographer = Choreographer.getInstance();
}
void tick() {
if (mStatus != MARQUEE_RUNNING) {
return;
}
mChoreographer.removeFrameCallback(mTickCallback);
final TextView textView = mView.get();
if (textView != null && (textView.isFocused() || textView.isSelected())) {
long currentMs = mChoreographer.getFrameTime();
long deltaMs = currentMs - mLastAnimationMs;
mLastAnimationMs = currentMs;
float deltaPx = deltaMs * mPixelsPerMs;
mScroll += deltaPx;
if (mScroll > mMaxScroll) {
mScroll = mMaxScroll;
mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);
} else {
mChoreographer.postFrameCallback(mTickCallback);
}
textView.invalidate();
}
}
看到 mChoreographer大佬就头疼,不忍卒读[doge]
- 最后还是来到最爱的RecyclerView心头肉环节。首先RecyclerView好处理图片数据的加载和
自定义,剩下的就是怎么动起来了。用CountDownTimer定时,然后定时smoothScrollToPosition,想想就很smooth,但效果不行,倏一下就滑动完了。还是老老实实用scrollBy吧,在很短时间内滑动很短距离,连起来的话就是连续光滑的滑动了,就像电影一样,好了,上代码
private fun initTimer() {
Log.e("initTimer===", "initTimer")
if (timer == null) {
//单个item宽度
mWidth = ll.width + ll.marginLeft + ll.marginRight//单个item宽度
//recyclerView宽度
mParentWidth = recyclerView.width - recyclerView.paddingLeft - recyclerView.paddingRight
//如果可以显示完整,返回
if (mData.size * mWidth < mParentWidth) {
return
}
//需要滚动的总长度
var xTotalScroll = mWidth * mData.size - mParentWidth + 50L
//滚动间隔
var interval = 50L
//每次滚动距离
var step = 20
//总共滚动时间
var time = xTotalScroll / step * interval
timer = object : CountDownTimer(time, interval) {
override fun onFinish() {
Handler().postDelayed({
//重置滚动状态
xScrolled = 0
recyclerView.scrollToPosition(0)
}, 1000)
realScroll()
}
override fun onTick(p0: Long) {
recyclerView.scrollBy(step, 0)
xScrolled += step
}
}
realScroll()
}
}
这样就完成跑马灯逻辑了,剩下就是关于性能优化的思考。上面的代码都是写在RecyclerView的adapter中的,所以在adapter的onAttachedToRecyclerView和onDetachedFromRecyclerView中就应该有相应的回收和初始化工作。
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
//初始化跑马灯
recyclerView.post {
initTimer()
}
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
//停止计时器
stopWork()
}
相应地,在Activity的onResume和onPause状态监听中也要做相应的工作,具体实现是通过adapter继承LifecycleObserver,面试常问jetpack的lifecycle,不得不说lifecycle就是好用
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
Log.e("onResume===", "onResume")
if (paused) {
recyclerView.post {
initTimer()
}
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
Log.e("onPause===", "onPause")
paused = true
stopWork()
}
对应的,在Activity中注册观察者
lifecycle.addObserver(mAdapter)