录的屏滑动有点卡,其实还是很丝滑的。
(半夜三点钟还在写博客,只希望这个系列博客可以帮助到更多人,后期还会一直更新,欢迎大家评论点赞,你们的支持是我永远的动力,嘿嘿。)
通过调用RecyclerView的smoothScrollToPosition(int position)方法我们可以实现平滑的滚动到参数所指定的position的位置,如果仅仅是调用了该方法那么滚动将会以系统默认的速度来进行,并且该速度通常是非常快的,快到如果将参数position的值设置的很小可能会看不到滚动的过程而直接看到结果,设置稍微大点例如为100时就会看到2秒的滚动效果,值越大滚动过程持续的时间越长,并且执行该方法滚动到该位置之后会立刻停止滚动,我们本篇文章讲的是RecyclerView如何实现自动无限滚动效果,因此我们可以这么来实现:
1,设置RecyclerView的item数量为int类型的最大值,这样滚动过程就会持续很长时间,会给用户造成一种错觉是无限滚动,其实也是单次的,等到滚动到int最大值指定的那个位置就会停止,但是一般用户不可能等那么长时间,如果不放心我们也可以在代码中设置实现循环滚动,
2,解决滚动速度的问题,前面说了,系统默认的滚动速度是非常快的,可能一秒钟就滚动过了几十个item甚至接近一百个item,滚动太快致使item内容在滚动过程中完全看不清楚,显然我们需求肯定不是这样的,我们要实现item的缓慢自动滚动,并且在滚动过程中要能清楚的看到每个item的内容,那么我们就需要改变系统默认的滚动速度,做法是自定义一个LayoutManager类继承LinearLayoutManager,然后复写smoothScrollToPosition方法。在该方法中创建一个Scroller对象,在Scroller对象中再重写calculateSpeedPerPixel(DisplaMetrics)方法,该方法就是用来控制滚动速度的,具体请看下面代码示例。
1,布局文件很简单就是定义了一个RecyclerView,MainActivity代码如下:
public class MainActivity extends AppCompatActivity {
AutoScrollRecyclerView recyclerView;
private List<String> list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
for(int i=0;i<30;i++){
list.add("我是条目"+i);
}
LinearLayoutManager manager = new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
// AutoScrollLayoutManager manager = new AutoScrollLayoutManager(this,LinearLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(manager);
// 设置适配器
Adapter mAdapter = new Adapter(this, list);
recyclerView.setAdapter(mAdapter);
// 调用该方法来触发自动滚动
recyclerView.smoothScrollToPosition(mAdapter.getItemCount());
}
class Adapter extends RecyclerView.Adapter<MainActivity.ViewHolder> {
private Context context;
private List<String> list;
public Adapter(Context context, List<String> list) {
this.context = context;
this.list = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_recycler, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
if(!list.isEmpty()){
// 因为getItemCount返回int最大值,而我们设置的数据不可能那么多,所以在为item绑定数据的时候,我们
// 这里采用取余的方法,这样就能保证每一项item都是有值的,但为了不重复就要求我们的样本数据多一点
holder.vText.setText(list.get(position%list.size()));
}
}
@Override
public int getItemCount() {
// 返回int最大值
return list == null ? 0 : Integer.MAX_VALUE;
}
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView vText;
public ViewHolder(View itemView) {
super(itemView);
vText = itemView.findViewById(R.id.tv);
}
}
}
注意上面getItemCount和onBindViewHolder方法在定义时的小技巧,上面注释也已经写的很清楚了。
运行程序会发现此时自动滚动已经实现了,但是滚动速度还没处理,还是系统默认的,所以非常快,几乎看不到条目内容,接下来我们来处理滚动的速度,方法如下:
自定义一个类AutoScrollLayoutManager继承LinearLayoutManager,然后复写smoothScrollToPosition方法。在该方法中创建一个Scroller对象,在Scroller对象中再重写calculateSpeedPerPixel(DisplaMetrics)方法,代码如下:
public class AutoScrollLayoutManager extends LinearLayoutManager {
public AutoScrollLayoutManager(Context context) {
super(context);
}
public AutoScrollLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public AutoScrollLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
// 修改系统默认的滚动速度需要实现该方法;
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int
position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
@Nullable
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return AutoScrollLayoutManager.this.computeScrollVectorForPosition
(targetPosition);
}
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
// 计算滑动每个像素需要的时间,这里应该与屏幕适配;
return 15f / displayMetrics.density;
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
}
然后在创建布局管理器时,使用我们自定义的AutoScrollLayoutManager,此时再运行应用我们会发现现在速度已经慢了好多了,条目内容也可以看得清楚了。
但是还有个问题就是当RecyclerView在自动滚动过程中当我们用手触摸到了,它就会立刻停止滚动,并且它还能响应我们的滑动事件,而大多数时候需求仅仅是自动滚动展示数据而已,要求屏蔽我们的触摸和滑动动作,那么此时我们就需要自定义RecyclerView,重写两个与事件相关的方法,代码如下示例:
public class AutoScrollRecyclerView extends RecyclerView {
public AutoScrollRecyclerView(Context context) {
super(context);
}
public AutoScrollRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public AutoScrollRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
// 拦截事件;
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
// 拦截触摸事件;
return true;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
// 消费事件;
return true;
}
}
至此再运行代码,效果基本上已经能够满足需求了,前面也分析过看似无限滑动其实也只是一种错觉,是我们将条目总数设置的很大(int的最大值)将滚动速度设置的很小给用户造成的错觉,其实本质还是单次滚动,滚动到int最大值的位置还是会停止,为了实现真正意义上的无限滚动,我们可以通过监听RecyclerView的滑动状态来实现循环滚动,即当滚动到最后一个item时,再次重新定位到开始位置,然后再次滑动到最后一个item,依次循环,代码如下例:
this.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
// 如果自动滑动到最后一个位置,则此处状态为SCROLL_STATE_IDLE
AutoScrollLayoutManager lm = (AutoScrollLayoutManager) recyclerView
.getLayoutManager();
int position = lm.findLastCompletelyVisibleItemPosition();
int count = lm.getItemCount();
if(position == count-1){
lm.scrollToPosition(0);
AutoScrollRecyclerView.this.smoothScrollToPosition(mAdapter.getItemCount());
}
}
}
});
至此就实现了列表自动无限滚动效果。