设计思路
ListView 上拉加载很容易实现,监听 ListView 滑动到底部,显示 FootView 即可,但是 RecyclerView 没有 addFootView(View view) 这个方法,有一种方案是在 adapter 中加一个 itemView 用于显示 FootView,但是这样做就得每一个使用到 RecyclerView 的地方都写一遍重复代码,而且不能适配多种类型的 LayoutManager,所以 RecyclerView 就需要另外一种思路来实现,我的解决方案是:自定义 PullToRefreshRecyclerView 继承 FrameLayout,将 RecyclerView 与 FootView 添加到 PullToRefreshRecyclerView 中,上拉时显示 FootView。
效果如下:
可以看到有个很明显的问题,FootView 是一直在屏幕内部的,即使隐藏掉滑动到底部在显示效果也很差劲,我的解决方案是重写 PullToRefreshRecyclerView 的 onMeasure 方法,将 PullToRefreshRecyclerView 高度设置为屏幕高度加上 FootView 的高度,上拉时使用 scrollTo 方法显示 FootView。
onMeasure 方法如下:
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int height = getMeasuredHeight() + footViewHeight;
setMeasuredDimension(getMeasuredWidth(), height);
LayoutParams rootLP = (LayoutParams) rootView.getLayoutParams();
rootLP.height = height;
rootView.setLayoutParams(rootLP);//设置 FootView的高度
LayoutParams recyclerLp = (LayoutParams) recyclerView.getLayoutParams();
recyclerLp.height = height - footViewHeight;
recyclerView.setLayoutParams(recyclerLp);//将 RecyclerView 的高度设置为屏幕高度
}
这样基本布局就完成了,然后就是对滑动事件的拦截与分发,如果滑动到底部则拦截事件,并且根据滑动距离来计算 scrollTo 的移动距离,具体代码如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (recyclerView == null || recyclerView.getChildCount() == 0)
return super.onInterceptTouchEvent(ev);
if(isLoading) return true;//如果正在加载中则拦截滑动事件
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastDownY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
int lastPosition = -1;
//以下代码用于获取当前 RecyclerView 中显示的最后一个 position
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
lastPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof LinearLayoutManager) {
lastPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] lastPositions = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(lastPositions);
lastPosition = findMax(lastPositions);
}
int offerY = (int) ev.getY() - lastDownY;
if (offerY < 0) {
//如果正在上拉