手把手教你实现RecyclerView实现上拉刷新功能
使用RecyclerView实现底部上拉刷新
项目地址:
http://xiaoniaojun.cn/2017/05/08/使用RecyclerView实现底部上拉刷新.html
首先来看一下效果吧:
onCreateViewHolder
首先来看一下Adapter中的onCreateViewHolder方法:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
我们重点关注viewType这个参数。在onCreateViewHolder回调时,系统会传入这个viewType,表示列表中每个项的View的类型。
这个viewType是个整形值,默认为0。我们可以通过在Adapter内重写public int getItemViewType(int position)方法来设置viewType。
这就是在RecyclerView中实现多类型View的关键。
如何实现上拉刷新
首先我们要考虑上拉刷新的过程:
在用户将RecyclerView拉到底部的时候,出现“正在刷新内容”的提示。此时,开始请求新内容并添加至Adapter中的数据容器列表中。
所以,这个过程里有如下几个关键点:
1.如何在列表底部现实“正在刷新内容”的提示View;
2.如何在到达列表底部的时候获得通知(回调);
3.如何动态地向RecyclerView中添加新的数据项目。
下面我们就来依次解决每一个关键点。
在列表底部现实“正在刷新内容”的提示View
可以这样实现,在底部永远都加入一个显示提示的LoadingMoreView:
为此,我们需要在Adapter中修改几个地方:
在getItemCount()方法中+1,因为添加了一个LoadingMoreView
@Overridepublic int getItemCount() { // 在列表底部添加了一个 View:Lading more
return mItems.size() + 1;
}
然后,为普通的Item View和LoadingMoreView设置viewType:
private static final int VIEW_TYPE_NORMAL = 1;
private static final int VIEW_TYPE_LOADING_MORE = 2;
// 复写此方法以确定viewType
@Override
public int getItemViewType(int position) {
int type = VIEW_TYPE_NORMAL;
// 如果当前position是列表底部,则为LoadingMoreView
if (position == mItems.size())
type = VIEW_TYPE_LOADING_MORE;
return type;
}
然后,还需要为这个LoadingMoreView编写ViewHodler。由于不需要修改其内容,所以写一个最简单的ViewHolder就可以了:
123456public class LoadingMoreVH extends RecyclerView.ViewHolder {
public LoadingMoreVH(View itemView) {
super(itemView);
}
}
之后,修改onCreateViewHolder和onBindViewHolder,主要是判断何时该处理不同的View,这里给出示例,大家自己体会一下:
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_NORMAL) {
View view = mInflater.inflate(R.layout.item_zhihu_home_first, parent, false);
final VH holder = new VH(view);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
if (mClickListener != null) {
mClickListener.onItemClick(position, v, holder);
}
}
});
return holder; // 处理LoadingMoreView
} else {
View view = mInflater.inflate(R.layout.item_load_more, parent, false);
return new LoadingMoreVH(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { // 避开LoadingMoreV
if (position < mItems.size()) {
Article item = mItems.get(position);
((VH) holder).img.setImageResource(item.getImgRes());
((VH) holder).tvTitle.setText(item.getTitle());
}
}
在到达列表底部的时候获得回调 及 动态地添加新的数据项目
RecyclerView并没有为我们提供类似的回调,所以我们需要手动实现它。 这里我们利用OnScrollListener()来实现。
// 在Activity中
private boolean loading = true;
int pastVisibleItems, visibleItemCount, totalItemCount;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mScrollTotal += dy;
if (dy > 0) {
visibleItemCount = mLayoutmanager.getChildCount();
totalItemCount = mLayoutmanager.getItemCount();
pastVisibleItems = mLayoutmanager.findFirstVisibleItemPosition();
if (loading) {
if (visibleItemCount + pastVisibleItems >= totalItemCount) { // 滚动到列表底部
loading = false; // 产生回调
onScrollReachedBottom();
}
}
}
}
});
在onScrolled()回调中,我们通过findFirstVisibleItemPosition和getChildCount()可以算出当前屏幕中,最底部项目的索引号。只需要将它与Adapter中的数据列表元素总数一比较,就可以得出是否滚动到了RecyclerView底部。
我们再写一个接口作为回调:
interface OnScrollReachedBottomListener {
void onScrollReachedBottom();
}
然后在当前Activity中实现这个接口就可以得到回调了。 下面是我的demo中的回调方法,在其中执行请求(网络)新数据,并通过:
// RecyclerView滚动到最后一个元素
@Override
public void onScrollReachedBottom() {
Log.v(TAG,"到达列表最后一个元素。"); // 模拟请求延迟
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() { // 产生mock数据
ArrayList<Article> list = new ArrayList<>();
for (int i = 0; i < 2; i++) {
int index = i % 2;
Article article = new Article(mTitles[index+5], mImgRes[index]);
list.add(article);
}
// 注意这个 -1,不要将数据插入到“正在载入”提示View之后去了
int insertPosition = mAdapter.getItemCount() - 1;
mAdapter.addDatas(list);
// 使用该方法更新数据,可以保持当前用户的滚动位置
mAdapter.notifyItemInserted(insertPosition);
loading = true;
}
},300);
}
为了要在Adapter中插入新数据,需要在Adapter中添加一个addDatas()方法:
public void addDatas(List<Article> items) {
mItems.addAll(items);
}
博客地址:
http://xiaoniaojun.cn/2017/05/08/使用RecyclerView实现底部上拉刷新.html
终端研发部提倡: 没有做不到的,只有想不到的。
在这里获得的不仅仅是技术!
这里学到不仅仅是技术