RecyclerView是Android5.0以后推出的新控件,相比于ListView可定制性更大,大有取代ListView之势。下面这篇博客主要来实现RecyclerView的上拉加载更多功能。
基本思路是让RecyclerView的Adapter加载两种布局,第一个布局来显示主界面,第二个布局来显示上拉加载时的提示信息,让RecyclerView监听是否滑动到最后一个item,如果是,则调用上拉刷新的逻辑,拉取远程数据,并显示第二个布局。等加载完毕时,刷新
Adapter,并隐藏第二个布局。下面分析代码。
要加载两种不同的布局,Adapter要重写getItemViewType方法。
@Override
public int getItemViewType(int position) {
if (position == dataList.size())
return 1;
else
return 0;
}
onCreateViewHolder根据viewtype加载ViewHolder.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == 0) {
view = LayoutInflater.from(context).inflate(R.layout.grid_redu_item, parent, false);
return new NormalHolder(view);
} else {
view = LayoutInflater.from(context).inflate(R.layout.sample_common_list_footer, parent, false);
mFooterHolder = new FooterHolder(view);
return mFooterHolder;
}
}
注意getItemCount长度要加1。
@Override
public int getItemCount() {
return dataList.size() + 1;
}
NormalHolder
class NormalHolder extends RecyclerView.ViewHolder {
ImageView img;
TextView name; public NormalHolder(View itemView) {
super(itemView);
img = (ImageView) itemView.findViewById(R.id.homepage_grid_picpic);
name = (TextView) itemView.findViewById(R.id.homepage_grid_name);
} public void setData(int position) {
imageLoader.displayImage(context, imgUrls[position % imgUrls.length], img);
name.setText(dataList.get(position));
}
}
FooterHolder
public class FooterHolder extends RecyclerView.ViewHolder {
View mLoadingViewstubstub;
View mEndViewstub;
View mNetworkErrorViewstub; public FooterHolder(View itemView) {
super(itemView);
mLoadingViewstubstub = itemView.findViewById(R.id.loading_viewstub);
mEndViewstub = itemView.findViewById(R.id.end_viewstub);
mNetworkErrorViewstub = itemView.findViewById(R.id.network_error_viewstub);
} //根据传过来的status控制哪个状态可见
public void setData(LoadingFooter.FooterState status) {
Log.d("TAG", "reduAdapter" + status + "");
switch (status) {
case Normal:
setAllGone();
break;
case Loading:
setAllGone();
mLoadingViewstubstub.setVisibility(View.VISIBLE);
break;
case TheEnd:
setAllGone();
mEndViewstub.setVisibility(View.VISIBLE);
break;
case NetWorkError:
setAllGone();
mNetworkErrorViewstub.setVisibility(View.VISIBLE);
break;
default:
break;
} } //全部不可见
void setAllGone() {
if (mLoadingViewstubstub != null) {
mLoadingViewstubstub.setVisibility(View.GONE);
}
if (mEndViewstub != null) {
mEndViewstub.setVisibility(View.GONE);
}
if (mNetworkErrorViewstub != null) {
mNetworkErrorViewstub.setVisibility(View.GONE);
}
} }
FooterHolder布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/loading_view"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:orientation="vertical"
tools:layout_height="wrap_content"> <include
android:id="@+id/loading_viewstub"
layout="@layout/sample_common_list_footer_loading"
android:layout_width="match_parent"
android:layout_height="40dp" /> <include
android:id="@+id/end_viewstub"
layout="@layout/sample_common_list_footer_end"
android:layout_width="match_parent"
android:layout_height="40dp" /> <include
android:id="@+id/network_error_viewstub"
layout="@layout/sample_common_list_footer_network_error"
android:layout_width="match_parent"
android:layout_height="40dp" />
</LinearLayout>
显示效果:
主要逻辑放在Fragment里。
首先我们要绑定为RecyclerView绑定一个监听器,监听RecyclerView是否滑到了底部。
Listener代码:
package com.yctime.truelove.LoadMore; import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View; import com.yctime.truelove.ImageLoader.UILPauseOnScrollListener; /**
* Created by xjx
* <p/>
* 继承自RecyclerView.OnScrollListener,一:可以监听到是否滑动到页面最低部。二:滑动时停止加载图片
*/
public class EndlessRecyclerOnScrollListener extends UILPauseOnScrollListener { /**
* 当前RecyclerView类型
*/
protected LayoutManagerType layoutManagerType; /**
* 最后一个的位置
*/
private int[] lastPositions; /**
* 最后一个可见的item的位置
*/
private int lastVisibleItemPosition; /**
* 当前滑动的状态
*/
private int currentScrollState = 0; @Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy); RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManagerType == null) {
if (layoutManager instanceof LinearLayoutManager) {
layoutManagerType = LayoutManagerType.LinearLayout;
} else if (layoutManager instanceof GridLayoutManager) {
layoutManagerType = LayoutManagerType.GridLayout;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
layoutManagerType = LayoutManagerType.StaggeredGridLayout;
} else {
throw new RuntimeException(
"Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
}
} switch (layoutManagerType) {
case LinearLayout:
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
break;
case GridLayout:
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
break;
case StaggeredGridLayout:
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
if (lastPositions == null) {
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
}
staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
lastVisibleItemPosition = findMax(lastPositions);
break;
}
} @Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
currentScrollState = newState;
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE && (lastVisibleItemPosition) >= totalItemCount - 1)) {
onLoadNextPage(recyclerView);
}
} /**
* 取数组中最大值
*
* @param lastPositions
* @return
*/
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
} return max;
} public void onLoadNextPage(final View view) {
} public static enum LayoutManagerType {
LinearLayout,
StaggeredGridLayout,
GridLayout
}
}
下面是Fragment完整代码:
package com.yctime.truelove.fragment; import android.support.v4.app.Fragment;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import com.yctime.truelove.LoadMore.EndlessRecyclerOnScrollListener;
import com.yctime.truelove.LoadMore.LoadingFooter;
import com.yctime.truelove.MainActivity;
import com.yctime.truelove.Utils.NetworkUtils;
import com.yctime.truelove.login.R; import java.util.ArrayList; /**
* A simple {@link Fragment} subclass.
*/
public class HomeReDuFragment extends BaseFragment { private RecyclerView mRecyclerView;
private GridAdapter_Redu gridReDuAdapter;
// 服务器端一共多少条数据
private static final int TOTAL_COUNTER = 50;
// 每一页展示多少条数据
private static final int REQUEST_COUNT = 12;
// 已经获取到多少条数据了
private int mCurrentCounter = 0;
//模拟的数据源
private ArrayList<String> dataList; protected LoadingFooter.FooterState mState = LoadingFooter.FooterState.Normal; protected void setState(LoadingFooter.FooterState mState) {
this.mState = mState;
((MainActivity) mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
changeAdaperState();
}
});
} //改变底部bottom的样式
protected void changeAdaperState() {
if (gridReDuAdapter != null && gridReDuAdapter.mFooterHolder != null) {
gridReDuAdapter.mFooterHolder.setData(mState);
}
} public HomeReDuFragment() {
} @Override
protected View initView() {
View mView = LayoutInflater.from(mContext).inflate(R.layout.homepage_viewpager_item_redu, null);
mRecyclerView = (RecyclerView) mView.findViewById(R.id.home_page_recyclerview);
return mView;
} @Override
protected void initData() {
initGridView();
} private View initGridView() {
mRecyclerView.setHasFixedSize(true);
//滑动暂停加载网络图片,而且可以监听recycler是否滑动到底部
mRecyclerView.addOnScrollListener(mOnScrollListener);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
gridReDuAdapter = new GridAdapter_Redu(mContext);
gridReDuAdapter.addAll(getRemoteData());
mRecyclerView.setAdapter(gridReDuAdapter);
GridLayoutManager layoutManager = new GridLayoutManager(mContext, 3);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
//如果是最后一个item,则设置占据3列,否则占据1列
boolean isFooter = position == gridReDuAdapter.getItemCount() - 1;
return isFooter ? 3 : 1;
}
});
mRecyclerView.setLayoutManager(layoutManager);
return mRecyclerView;
} private EndlessRecyclerOnScrollListener mOnScrollListener = new EndlessRecyclerOnScrollListener() {
@Override
public void onLoadNextPage(View view) {
super.onLoadNextPage(view); if (mState == LoadingFooter.FooterState.Loading) {
Log.d("@TAG", "the state is Loading, just wait..");
return;
} if (mCurrentCounter < TOTAL_COUNTER) {
// loading more
requestData();
Log.d("TAG", "请求数据");
} else {
//the end
setState(LoadingFooter.FooterState.TheEnd);
}
}
}; /**
* 模拟请求网络
*/
private void requestData() {
setState(LoadingFooter.FooterState.Loading);
new Thread() {
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (NetworkUtils.isNetAvailable(mContext)) {
//模拟请求远程数据
gridReDuAdapter.addAll(getRemoteData());
//加载完毕时
setState(LoadingFooter.FooterState.Normal);
Log.d("TAG", mCurrentCounter + "");
} else {
//模拟一下网络请求失败的情况
setState(LoadingFooter.FooterState.NetWorkError);
}
}
}.start();
} //模拟请求数据
private ArrayList<String> getRemoteData() {
if (dataList == null)
dataList = new ArrayList<>();
//每次都清空一下
dataList.clear();
//要减去adapter最后一页
for (int i = 0; i < REQUEST_COUNT; i++) {
if (dataList.size() + mCurrentCounter >= TOTAL_COUNTER) {
break;
}
dataList.add("账号" + (mCurrentCounter + i));
}
mCurrentCounter += dataList.size();
return dataList;
} }
需要注意的是这个方法:
//改变底部bottom的样式
protected void changeAdaperState() {
if (gridReDuAdapter != null && gridReDuAdapter.mFooterHolder != null) {
gridReDuAdapter.mFooterHolder.setData(mState);
}
}
Adapter的应用调用Adapter里面的方法,来切换Adaper的样式。
Adapter完整代码:
package com.yctime.truelove.fragment; import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView; import com.yctime.truelove.ImageLoader.MyImageLoader;
import com.yctime.truelove.ImageLoader.UILImageLoader;
import com.yctime.truelove.LoadMore.LoadingFooter;
import com.yctime.truelove.drawer.MyZoneActivity;
import com.yctime.truelove.login.R; import java.util.ArrayList; public class GridAdapter_Redu extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public String[] imgUrls = {
"http://img5.duitang.com/uploads/item/201402/22/20140222113830_X3ddd.jpeg",
"http://v1.qzone.cc/avatar/201403/01/10/36/531147afa4197738.jpg!200x200.jpg",
"http://g.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=e4d7ed147af40ad115b1cfe7621c3de9/b7fd5266d016092445b47837d50735fae6cd340d.jpg",
"http://img5q.duitang.com/uploads/item/201502/19/20150219182507_vGVaK.jpeg",
"http://p1.qqyou.com/touxiang/uploadpic/2013-3/12/2013031212190118646.jpg",
"http://img5.duitang.com/uploads/item/201412/08/20141208221323_YVJFk.png",
"http://cdn.duitang.com/uploads/item/201408/02/20140802222651_GWuU2.png",
"http://ent.dzwww.com/yulezhuanti/mtcbg/201510/W020151027467479100669.jpg",
"http://p1.qqyou.com/touxiang/uploadpic/2013-3/10/2013031009323656495.jpg",
"http://p1.qqyou.com/touxiang/uploadpic/2013-3/12/2013031212295986807.jpg",
"http://f.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=10742594d739b6004d9b07b1d9601912/9f2f070828381f30ec9eabdeab014c086f06f0c5.jpg",
"http://a.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=5bda8a18a71ea8d38a777c02a73a1c76/5882b2b7d0a20cf4598dc37c77094b36acaf9977.jpg",
"http://a1.att.hudong.com/36/98/300001051406133039983418031.jpg"
};
public Context context;
MyImageLoader imageLoader = new UILImageLoader();
private ArrayList<String> dataList = new ArrayList<>(); private final int NORMALLAYOUT = 0;
private final int FOOTERLAYOUT = 1;
public FooterHolder mFooterHolder; public GridAdapter_Redu(Context context) {
this.context = context;
} public void addAll(ArrayList<String> list) {
int lastIndex = this.dataList.size();
if (this.dataList.addAll(list)) {
notifyItemRangeInserted(lastIndex, list.size());
}
} @Override
public int getItemViewType(int position) {
if (position == dataList.size())
return FOOTERLAYOUT;
else
return NORMALLAYOUT;
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == NORMALLAYOUT) {
view = LayoutInflater.from(context).inflate(R.layout.grid_redu_item, parent, false);
return new NormalHolder(view);
} else {
view = LayoutInflater.from(context).inflate(R.layout.sample_common_list_footer, parent, false);
mFooterHolder = new FooterHolder(view);
return mFooterHolder;
}
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof NormalHolder) {
//点击事件
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, MyZoneActivity.class);
context.startActivity(intent);
}
});
((NormalHolder) holder).setData(position);
}
} @Override
public int getItemCount() {
return dataList.size() + 1;
} class NormalHolder extends RecyclerView.ViewHolder {
ImageView img;
TextView name; public NormalHolder(View itemView) {
super(itemView);
img = (ImageView) itemView.findViewById(R.id.homepage_grid_picpic);
name = (TextView) itemView.findViewById(R.id.homepage_grid_name);
} public void setData(int position) {
imageLoader.displayImage(context, imgUrls[position % imgUrls.length], img);
name.setText(dataList.get(position));
}
} public class FooterHolder extends RecyclerView.ViewHolder {
View mLoadingViewstubstub;
View mEndViewstub;
View mNetworkErrorViewstub; public FooterHolder(View itemView) {
super(itemView);
mLoadingViewstubstub = itemView.findViewById(R.id.loading_viewstub);
mEndViewstub = itemView.findViewById(R.id.end_viewstub);
mNetworkErrorViewstub = itemView.findViewById(R.id.network_error_viewstub);
} //根据传过来的status控制哪个状态可见
public void setData(LoadingFooter.FooterState status) {
Log.d("TAG", "reduAdapter" + status + "");
switch (status) {
case Normal:
setAllGone();
break;
case Loading:
setAllGone();
mLoadingViewstubstub.setVisibility(View.VISIBLE);
break;
case TheEnd:
setAllGone();
mEndViewstub.setVisibility(View.VISIBLE);
break;
case NetWorkError:
setAllGone();
mNetworkErrorViewstub.setVisibility(View.VISIBLE);
break;
default:
break;
} } //全部不可见
void setAllGone() {
if (mLoadingViewstubstub != null) {
mLoadingViewstubstub.setVisibility(View.GONE);
}
if (mEndViewstub != null) {
mEndViewstub.setVisibility(View.GONE);
}
if (mNetworkErrorViewstub != null) {
mNetworkErrorViewstub.setVisibility(View.GONE);
}
} } }
运行效果: