RecycleView 滑动到底部,加载更多
android.support.v7 包提供了一个新的组件:RecycleView,用以提供一个灵活的列表试图、显示大型数据集,它支持局部刷新、显示动画等功能,可以用来取代ListView与GridView.
然而在使用的过程中却遇到一些问题,基本现在手机页面都会有滑动到底部加载更多的功能,而RecycleView 并没有提供像ListView的addFooter等方法,所以实现起来还是有点麻烦。经过一段时间的摸索,终于找到实现RecycleView加载更多的方法。
本文主要通过RecycleView 的 ItemViewType 来实现RecycleView滑动到底部加载更多功能,支持 列表、网格、瀑布流等布局。
根据ItemViewType创建两种布局,一个用于显示正常内容的布局,一个用于加载布局,然后监听RecycleView的滚动事件,当滑动到底部时添加加载布局,对于列表布局来说很简单,不再熬述,然而对于网格布局与瀑布流布局来说,要解决"加载布局只占用一列"的问题,
1.对于网格布局,GridLayoutManager 提供了一个 setSpanSizeLookup() 的方法,用来设置每个条目可以占用的列数,默认为1.
2.对于瀑布流,StaggeredGridLayoutManager 提供了一个 StaggeredGridLayoutManager.LayoutParams 内部静态类,此类中有一个setFullSpan() 方法,用来设置条目跨越全列。
3.基类的实现方式如下:子类只需要重写以下方法:
onCreateNormalViewHolder(ViewGroup parent);
onBindNormalViewHolder(RecyclerView.ViewHolder holder, int position);
/**
* Created by sunwei on 2015/12/4.
* Email: lx_sunwei@163.com.
* Description: recycleView 滑动到底部加载更多
*/
public abstract class BaseLoadingAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final String TAG = "BaseLoadingAdapter"; //是否正在加载
public boolean mIsLoading = false;
//正常条目
private static final int TYPE_NORMAL_ITEM = 0;
//加载条目
private static final int TYPE_LOADING_ITEM = 1;
//加载viewHolder
private LoadingViewHolder mLoadingViewHolder;
//瀑布流
private StaggeredGridLayoutManager mStaggeredGridLayoutManager;
//数据集
private CircularArray<T> mTs;
//首次进入
private boolean mFirstEnter = true;
private RecyclerView mRecyclerView; public BaseLoadingAdapter(RecyclerView recyclerView, CircularArray<T> ts) { mTs = ts; mRecyclerView = recyclerView; setSpanCount(recyclerView); //notifyLoading();
} private OnLoadingListener mOnLoadingListener; /**
* 加载更多接口
*/
public interface OnLoadingListener {
void loading();
} /**
* 设置监听接口
*
* @param onLoadingListener onLoadingListener
*/
public void setOnLoadingListener(OnLoadingListener onLoadingListener) {
setScrollListener(mRecyclerView);
mOnLoadingListener = onLoadingListener;
} /**
* 加载完成
*/
public void setLoadingComplete() {
if (mTs.size() > 0 && mTs.getLast() == null) {
mIsLoading = false;
mTs.removeFromEnd(1);
notifyItemRemoved(mTs.size() - 1);
}
} /**
* 没有更多数据
*/
public void setLoadingNoMore() {
mIsLoading = false;
if (mLoadingViewHolder != null) {
mLoadingViewHolder.progressBar.setVisibility(View.GONE);
mLoadingViewHolder.tvLoading.setText("已加载完!");
}
} /**
* 加载失败
*/
public void setLoadingError() {
if (mLoadingViewHolder != null) {
mIsLoading = false;
mLoadingViewHolder.progressBar.setVisibility(View.GONE);
mLoadingViewHolder.tvLoading.setText("加载失败,点击重新加载!"); mLoadingViewHolder.tvLoading.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnLoadingListener != null) {
mIsLoading = true;
mLoadingViewHolder.progressBar.setVisibility(View.VISIBLE);
mLoadingViewHolder.tvLoading.setText("正在加载..."); mOnLoadingListener.loading();
}
}
});
}
} /**
* @return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
*/
private boolean canScrollDown(RecyclerView recyclerView) {
return ViewCompat.canScrollVertically(recyclerView, 1);
} /**
* 设置加载item占据一行
*
* @param recyclerView recycleView
*/
private void setSpanCount(RecyclerView recyclerView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager == null) {
Log.e(TAG, "LayoutManager 为空,请先设置 recycleView.setLayoutManager(...)");
} //网格布局
if (layoutManager instanceof GridLayoutManager) {
final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = getItemViewType(position);
if (type == TYPE_NORMAL_ITEM) {
return 1;
} else {
return gridLayoutManager.getSpanCount();
}
}
});
} //瀑布流布局
if (layoutManager instanceof StaggeredGridLayoutManager) {
mStaggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
}
} /**
* 显示加载
*/
private void notifyLoading() {
if (mTs.size() != 0 && mTs.getLast() != null) {
mTs.addLast(null);
notifyItemInserted(mTs.size() - 1);
}
} /**
* 监听滚动事件
*
* @param recyclerView recycleView
*/
private void setScrollListener(RecyclerView recyclerView) {
if(recyclerView == null) {
Log.e(TAG, "recycleView 为空");
return;
} recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
} @Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy); if (!canScrollDown(recyclerView)) { //首次进入不加载
if (!mIsLoading && !mFirstEnter) { notifyLoading(); mIsLoading = true; if (mLoadingViewHolder != null) {
mLoadingViewHolder.progressBar.setVisibility(View.VISIBLE);
mLoadingViewHolder.tvLoading.setText("正在加载...");
} if (mOnLoadingListener != null) {
mOnLoadingListener.loading();
}
}
} if (mFirstEnter) {
mFirstEnter = false;
}
}
});
} /**
* 创建viewHolder
*
* @param parent viewGroup
* @return viewHolder
*/
public abstract RecyclerView.ViewHolder onCreateNormalViewHolder(ViewGroup parent); /**
* 绑定viewHolder
*
* @param holder viewHolder
* @param position position
*/
public abstract void onBindNormalViewHolder(RecyclerView.ViewHolder holder, int position); /**
* 加载布局
*/
private class LoadingViewHolder extends RecyclerView.ViewHolder {
public ProgressBar progressBar;
public TextView tvLoading;
public LinearLayout llyLoading; public LoadingViewHolder(View view) {
super(view); progressBar = (ProgressBar) view.findViewById(R.id.progress_loading);
tvLoading = (TextView) view.findViewById(R.id.tv_loading);
llyLoading = (LinearLayout) view.findViewById(R.id.lly_loading);
}
} @Override
public int getItemViewType(int position) {
T t = mTs.get(position);
if (t == null) {
return TYPE_LOADING_ITEM;
} else {
return TYPE_NORMAL_ITEM;
}
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_NORMAL_ITEM) {
return onCreateNormalViewHolder(parent);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.loading_layout, parent, false);
mLoadingViewHolder = new LoadingViewHolder(view);
return mLoadingViewHolder;
}
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int type = getItemViewType(position);
if (type == TYPE_NORMAL_ITEM) {
onBindNormalViewHolder(holder, position);
} else { if (mStaggeredGridLayoutManager != null) {
StaggeredGridLayoutManager.LayoutParams layoutParams =
new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setFullSpan(true); mLoadingViewHolder.llyLoading.setLayoutParams(layoutParams);
}
}
} @Override
public int getItemCount() {
return mTs.size();
}
}
Base Loading Adapter
4.一个简单的列子
/**
* Created by sunwei on 2015/12/4.
* Email: lx_sunwei@163.com.
* Description: 滑动到底部加载更多
*/
public class DesignLoaderMoreAdapter extends BaseLoadingAdapter<DesignItem> { private CircularArray<DesignItem> mDesignItems; public DesignLoaderMoreAdapter(RecyclerView recyclerView, CircularArray<DesignItem> datas) {
super(recyclerView, datas); mDesignItems = datas;
} //正常条目
public class DesignViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public CardView cardView;
public DesignViewHolder(View view) {
super(view);
textView = (TextView) view.findViewById(R.id.tv_design);
cardView = (CardView) view.findViewById(R.id.cardView_designer); }
} @Override
public RecyclerView.ViewHolder onCreateNormalViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_item_design, parent, false);
return new DesignViewHolder(view);
} @Override
public void onBindNormalViewHolder(RecyclerView.ViewHolder holder, int position) {
DesignViewHolder viewHolder = (DesignViewHolder)holder;
DesignItem designItem = mDesignItems.get(position);
if (position == 10) {
//设置瀑布流的条目大小
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(260, 360);
lp.setMargins(10, 40, 10, 80);
viewHolder.cardView.setLayoutParams(lp);
} viewHolder.textView.setText(designItem.name);
}
}
Sample

项目地址:https://github.com/lxsunwei/MaterialDesign
RecycleView 滑动到底部,加载更多的更多相关文章
- 滚动到底部加载更多及下拉刷新listview的使用
最新内容建议直接访问原文:滚动到底部加载更多及下拉刷新listview的使用 本文主要介绍可同时实现下拉刷新及滑动到底部加载更多的ListView的使用. 该ListView优点包括:a. 可自定义下 ...
- XRecyclerView:实现下拉刷新、滚动到底部加载更多以及添加header功能的RecyclerView
介绍: 一个实现了下拉刷新,滚动到底部加载更多以及添加header功能的的RecyclerView.使用方式和RecyclerView完全一致,不需要额外的layout,不需要写特殊的adater. ...
- ionic 上拉加载更多&瀑布流加载&滚动到底部加载更多 主意事项
首先下拉刷新的代码是这样的,标红的地方为关键代码 <html> <head> <meta charset="utf-8"> <meta n ...
- jquery页面滑到底部加载更多
$(window).scroll(function(){ var _scrolltop = $('body').scrollTop();if(_scrolltop+_winHeight>_doc ...
- Jquery实现滚动到底部加载更多(最原始)
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...
- vant list列表滚动到底部加载更多会滚动到顶部问题
如果使用异步加载数据并使用了vant中的toast做加载中提示,则有可能会导致列表滚动高度为0,也就是回到了顶部.只要在list加载回调里不使用toast就可以避免这个问题.
- JQuery ajax 滚动底部加载更多
<%@ Page Language="C#" %> <%@ Import Namespace="System.IO" %> <%@ ...
- RecyclerView实例-实现可下拉刷新上拉加载更多并可切换线性流和瀑布流模式(1)
摘要 最近项目有个列表页需要实现线性列表和瀑布流展示的切换,首先我想到的就是上 [RecyclerView],他本身已经很好的提供了三种布局方式,只是简单做个切换应该是很简单的事情,如果要用Recyc ...
- ListView下拉刷新上拉加载更多实现
这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...
随机推荐
- JavaScript重复元素处理
判断一个字符串中出现次数最多的字符,统计这个次数 //将字符串的字符保存在一个hash table中,key是字符,value是这个字符出现的次数 var str = "abcdefgadd ...
- php面向对象编程--学习笔记
1.声明一个类 在php中使用class关键字创建一个新类,类包括属性与方法.语法格式如下: <?php class类名{ 属性: 方法: } ?> 2.创建一个实例对象 创建对象的过程称 ...
- storm接入metaq时类不兼容错误 无法初始化MonitorLog
在实际的项目中,需要storm从metaq中读取数据,一开始就有日志问题,查到是storm/lib中的log4j-over-slf4j和slf4j-api.jar包的不兼容导致的,这些日志包使用时必须 ...
- 学c语言做练习之文件
打开两个文件,让程序打印第一个文件的第一行,第二个文件的第一行,第一个文件的第二行,第二个文件的第二行,依此类推,直到打印完行数较多的文件的最后一行. #include<stdio.h> ...
- readn、write、readline
字节流套接字上的read和write函数所表现的行为不同于通常的文件IO 字节流套接字上调用read或write输入或输出的字节数可能比请求的数量少,然而这不是出错的状态 这个现象的原因在于内核中用于 ...
- MYSQL用SOURCE命令时导入乱码的问题解决
现在遇到了,但记得上次辉哥说过的方法,在MYSQL命令行里输入: set names utf8; 再执行SOURCE命令,搞定!
- Java IO - BufferedReader & BufferedWriter
java.io 包提供丰富的 IO 读写功能,封装在不同的类里面.其中,使用 BufferedReader 和 BufferedWriter 可以方便地进行读.写文件的操作. 使用例子如下: impo ...
- 线性表(gcc实现)
线性结构: ①存在一个唯一的被称为“第一个”的数据元素: ②存在一个唯一的被称为“最后一个”的数据元素: ③除第一个元素外,每个元素均有唯一一个直接前驱: ④除最后一个元素外,每个元素均有唯一一个直接 ...
- objc_msgSend iOS8 EXC_BAD_ACCESS
如果方法是没有返回值的,需要强转一个返回类型为void的临时函数指针, void (*objc_msgSendTyped)(id self, SEL _cmd, id obj, id arg1) = ...
- ANT打包 小实例
ANT打包 package src; public class Hello { /** * @param args */ public static void main(String[] a ...