最近自己做一个小项目的时候遇到了RecyclerView的上拉加载问题,之前ListView用多了,上拉加载也有很完善的库了,可是RecyclerView我在Github上搜了一搜,没发现有特别好,特别完善的库,所以我就自己网上找了一些资料。然后自己尝试去写,结果悲剧的发现,RecyclerView最关键的居然是Adapter和ViewHolder,所以我就先从这两个入手。
首先是ViewHolder,大家都知道,RecyclerView中已经有了ViewHolder类了,所以我们可以简单封装一下,就想ListView的万能ViewHolder一样封装一下:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;
public class BaseViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;//集合类,layout里包含的View,以view的id作为key,value是view对象
private Context mContext;//上下文对象
public BaseViewHolder(Context context,View itemView) {
super(itemView);
mContext = context;
mViews = new SparseArray<>();
}
//通过View的id获得View
public <T extends View> T getView(int viewId){
//从集合中找View,找不到再去itemView中findViewById然后添加在集合类中
View view = mViews.get(viewId);
if (view == null){
view = itemView.findViewById(viewId);
mViews.put(viewId,view);
}
return (T)view;
}
//通过TextView的Id找到TextView然后直接设置TextView的Text属性
public BaseViewHolder setText(int viewId,String value){
TextView textView = getView(viewId);
textView.setText(value);
return this;
}
//通过TextView的Id找到TextView然后直接设置TextView的Text属性
public BaseViewHolder setText(int viewId,int resId){
TextView textView = getView(viewId);
textView.setText(resId);
return this;
}
}
对ViewHolder做了一下简单的封装,然后接下来就是重点了,写一个抽象的Adapter集成RecyclerView.Adapter,为了更好的使用,我们把这个Adapter做的全面一点,支持Header和Footer,下面就直接贴代码吧:
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private int mLayoutId;
private List<T> mDatas;
private LayoutInflater mInflater;
private boolean isLoading = false;
private OnItemClickListener mClickListener;
private OnItemLongClickListener mLongClickListener;
private OnLoadMoreListener mOnLoadMoreListener;
private View mHeaderViwe;
private boolean HaveMoreData = true;
private TextView LoadText;
/**
* 声明Item的状态
*/
private final static int TYPE_HEADVIEW=100;
private final static int TYPE_ITEM=101;
private final static int TYPE_FOOTER=102;
public BaseRecyclerAdapter(Context context, RecyclerView recyclerView, List<T> datas, int layoutId){
this.mLayoutId = layoutId;
this.mDatas = datas;
this.mContext = context;
mInflater = LayoutInflater.from(context);
init(recyclerView);
}
//获取Item总数
@Override
public int getItemCount() {
return mDatas.size() + 1;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//根据viewType去判断Item的类型,创建相对应的ViewHolder
if (viewType == TYPE_ITEM){
View itemView = mInflater.inflate(mLayoutId,parent,false);
BaseViewHolder holder = new BaseViewHolder(mContext,itemView);
return holder;
}else if (viewType == TYPE_HEADVIEW){
HeaderViewHolder headViewHolder=new HeaderViewHolder(mHeaderViwe);
return headViewHolder;
}
else{
View progressView = mInflater.inflate(R.layout.list_foot_loading,parent,false);
ProgressViewHolder progressHolder = new ProgressViewHolder(progressView);
return progressHolder;
}
}
/**
* 通过position判断返回的Item的类型
* @param position 索引
* @return 100返回的是头部布局,101返回的是数据源布局,102返回的是底部布局
*/
@Override
public int getItemViewType(int position) {
//判断mHeaderView是否为空决定是否要添加头部布局
if (mHeaderViwe!=null){
if (position==getItemCount()-1){
return TYPE_FOOTER;
}else if (position==0){
return TYPE_HEADVIEW;
}else {
return TYPE_ITEM;
}
}else {
if (position==getItemCount()-1){
return TYPE_FOOTER;
}else {
return TYPE_ITEM;
}
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof BaseViewHolder){
//提醒用户复写此方法
conver(mContext,holder,mDatas.get(position));
//注册点击事件
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mClickListener.onItemClick(v,position);
}
});
//注册长按事件
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mLongClickListener.onItemLongClick(v,position);
return true;
}
});
}
}
private void init(RecyclerView recyclerView){
//为RecyclerView添加滚动监听事件
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
//获得Item的总数
int totalItemCount = linearLayoutManager.getItemCount();
//获得最后一项的索引
int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
if (HaveMoreData && !isLoading && dy > 0 && lastVisibleItemPosition >= totalItemCount - 1){
//此时是刷新状态
if (mOnLoadMoreListener != null){
mOnLoadMoreListener.OnLoadMore();
}
isLoading = true;
}
}
});
}
public void updateData(List<T> data) {
mDatas.clear();
mDatas.addAll(data);
notifyDataSetChanged();
}
public void addAll(List<T> datas){
mDatas.addAll(datas);
notifyDataSetChanged();
}
/**
* 加载更多ViewHolder
*/
public class ProgressViewHolder extends RecyclerView.ViewHolder{
public ProgressViewHolder(View itemView) {
super(itemView);
LoadText = (TextView) itemView.findViewById(R.id.load_text);
}
}
/**
* 头部ViewHolder
*/
public class HeaderViewHolder extends RecyclerView.ViewHolder{
public HeaderViewHolder(View itemView) {
super(itemView);
}
}
/**
* 声明接口
*/
public interface OnItemClickListener{
public void onItemClick(View view,int position);
}
public interface OnItemLongClickListener{
public void onItemLongClick(View view,int position);
}
public interface OnLoadMoreListener{
public void OnLoadMore();
}
/**
* 设置监听事件
*/
public void setClickListener(OnItemClickListener clickListener) {
mClickListener = clickListener;
}
public void setLongClickListener(OnItemLongClickListener longClickListener) {
mLongClickListener = longClickListener;
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
mOnLoadMoreListener = onLoadMoreListener;
}
public void setLoading(boolean loading) {
isLoading = loading;
}
public void setHaveMoreData(boolean haveMoreData) {
HaveMoreData = haveMoreData;
//必须判断一下LoadText是否为空,因为一进入App时刷新布局并没有创建这LoadText
if (LoadText != null) {
if (!haveMoreData) {
LoadText.setText("没有更多了");
} else {
LoadText.setText("正在加载....");
}
}
}
public void addHeader(View headerViwe){
this.mHeaderViwe = headerViwe;
}
/**
* 声明抽象事件
*/
public abstract void conver(Context context,RecyclerView.ViewHolder holder,T t);
}
这样就把RecyclerView最重要的Adapter和ViewHolder写好了。
目前这个Adapter只适用于LinearLayoutManager布局,我会继续研究一下其他布局。。
然后就是使用了,使用的话,相对就简单一点啦,因为该封装的都封装了:
Adapter.java:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import java.util.List;
public class Adapter extends BaseRecyclerAdapter<String> {
public Adapter(Context context, RecyclerView recyclerView, List<String> datas, int layoutId) {
super(context, recyclerView, datas, layoutId);
}
@Override
public void conver(Context context, RecyclerView.ViewHolder holder, String s) {
//必须要判断holder是否是BaseViewHolder实例,不然会有可能导致布局混乱
if (holder instanceof BaseViewHolder){
((BaseViewHolder) holder).setText(R.id.tv,s);
}
}
}
MainActivity.java
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private Adapter mAdapter;
private List<String> mDatas = new ArrayList<>();
private int page=0;
private SwipeRefreshLayout refreshLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化布局
mRecyclerView = (RecyclerView) findViewById(R.id.rv);
refreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh);
//设置RecyclerView布局
LinearLayoutManager layout = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layout);
//初始化Adapter
mAdapter = new Adapter(this,mRecyclerView,mDatas,R.layout.list_item);
//为RecyclerView绑定适配器
mRecyclerView.setAdapter(mAdapter);
//添加上啦加载更多事件监听
mAdapter.setOnLoadMoreListener(new BaseRecyclerAdapter.OnLoadMoreListener() {
@Override
public void OnLoadMore() {
page++;
//新建一个线程去执行
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final List<String> data = new ArrayList<String>();
for (int i = 0; i < 6; i++) {
data.add("page" + page + "item" + i);
}
//使用UI线程去执行添加数据操作
//addData()是在自定义的Adapter中自己添加的方法,用来给list添加数据
runOnUiThread(new Runnable() {
@Override
public void run() {
if(mDatas.size() <= 30) {
mAdapter.addAll(data);
mAdapter.setLoading(false);
}
else{
mAdapter.setHaveMoreData(false);
mAdapter.setLoading(false);
}
}
});
}
}.start();
}
});
//添加Item的点击事件监听
mAdapter.setClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "this is "+position, Toast.LENGTH_SHORT).show();
}
});
//添加下拉刷新的事件监听
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//新建一个线程去执行
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final List<String> data = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
data.add("refresh item"+i);
}
//使用UI线程去执行数据更新操作
runOnUiThread(new Runnable() {
@Override
public void run() {
//调用adapter中定义的更新数据的方法
mAdapter.updateData(data);
refreshLayout.setRefreshing(false);
//设置一下HaveMoreData的
mAdapter.setHaveMoreData(true);
}
});
}
}.start();
}
});
//初始化mData的数据
for (int i=0;i<=9;i++){
mDatas.add("item"+i);
}
}
}
我写的这个例子是避免了重写RecyclerView,而通过Adapter去实现了上拉加载的方法,所以Adapter初始化的时候要把RecyclerView加进去,这个例子还有很多东西没有完善,但是基本的都有了,可以多多参考一下。有问题欢迎我们一起讨(tao)论(lu)一下!!