由于RecyclerView不支持自动滚动,那么首先我们需要进行一些自定义操作。
让RecyclerView自动滚动有两种思路。
方法1:添加属性动画,每次动画回调监听滚动1PX
方法2:使用postDelayed每隔一段时间发送一条消息,滚动RecyclerView。
经过试验验证,二者优劣如下表格所示:
方法 | 优缺点 |
---|---|
1 | 1.实现简单,只需要一个属性动画即可 2.动画的回调间隔无法控制,会存在动画一直在回调。同时根据手机处理的动画的速度,会出现时快时慢的现象。 3.在动画回调中直接调用recyclerView.scrollBy(1,0)存在无法滚动的问题(原因暂未探查) |
1 | 1.由于上述3的问题,所以直接通过postDelayed进行循环滚动的实现成了最主要原因。 2.能够大体控制时间的间隔 |
确定技术方案,编码实现如下:
public class AutoRecyclerView extends RecyclerView {
private static final long TIME_AUTO_POLL = 16;
private final AutoPollTask autoPollTask;
private boolean running; //表示是否正在自动轮询
private boolean canRun;//表示是否可以自动轮询
public AutoRecyclerView(@NonNull Context context) {
super(context);
autoPollTask = new AutoPollTask(this);
}
public AutoRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
autoPollTask = new AutoPollTask(this);
}
public AutoRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
autoPollTask = new AutoPollTask(this);
}
private static class AutoPollTask implements Runnable {
private final WeakReference<AutoRecyclerView> mReference;
//使用弱引用持有外部类引用->防止内存泄漏
public AutoPollTask(AutoRecyclerView reference) {
this.mReference = new WeakReference<>(reference);
}
@Override
public void run() {
AutoRecyclerView recyclerView = mReference.get();
if (recyclerView != null && recyclerView.running && recyclerView.canRun) {
//水平移动
recyclerView.scrollBy(2, 0);
//竖直移动
//recyclerView.scrollBy(0, 2);
recyclerView.postOnAnimationDelayed(recyclerView.autoPollTask, TIME_AUTO_POLL);
}
}
}
//开启:如果正在运行,先停止->再开启
private void start() {
if (running)
stop();
canRun = true;
running = true;
postDelayed(autoPollTask, TIME_AUTO_POLL);
}
private void stop() {
running = false;
removeCallbacks(autoPollTask);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return true;
}
public void startAutoScrolling(){
if (running){
return;
}
start();
}
public void stopAutoScrolling(){
stop();
}
public boolean isAutoScrolling(){
return running;
}
}
注:
1.时间间隔控制在16,是由于想保证一秒60帧的刷新率。保证每一帧都在运动。
2.最终编码采用的postOnAnimationDelayed而不是postDelayed主要是因为,postOnAnimationDelayed能够最大限度的保证动画的每一帧的执行。
附:
View.post 与View.postOnAnimation 有什么区别?
答:
view.post() 正常的向 Looper 中加入一条 Msg,遵守 Handler 消息执行逻辑。
view.postOnAnimation,向 Choreographer 中添加 CALLBACK_ANIMATION 类型的回调,该回调会在Choreographer 的一条异步消息中执行,在 VSYNC 信号到来时即会执行,即下一帧绘制之前就会执行,而 view.post() 提交的消息要比view.postOnAnimation延后一点执行。
简单一句话概述:post()会将runnable对象放在队列中等待执行。 postOnAnimation()方法会在下一帧立即执行runnable对象