[原创]Android实现图片列表跑马灯效果

  • 需求是图标控件宽度固定,控件内展示不全的话,内容会自动滚动到末尾,然后循环。
效果图
  • 首先想到是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(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)
  • 代码地址 https://github.com/meiniepan/MapRecord/blob/master/app/src/main/java/com/solang/maprecord/adapter/ImageMarqueeAdapter.kt
  • 总而言之,抛砖引玉。

你可能感兴趣的:([原创]Android实现图片列表跑马灯效果)