RecyclerView作为用来取代ListView的列表,其使用越来越广泛。这篇博文结合前辈文章补充说明其对上拉加载更多,下拉刷新的一种实现。
一、实现方法说明
关于下拉刷新,可以用谷歌提供的SwipeRefreshLayout来实现,而对于上拉加载更多观察RecyclerView.Adapter的onCreateViewHolder(ViewGroup parent, int viewType)中的viewType参数可添加一FootViewHolder来实现。
这种下拉刷新,上拉加载跟多的实现方式其优点是实现简单、代码较少,缺点是Adapter实现会比较复杂一点。
二、自定义Adapter实现上拉加载更多
1、这里定义一个DishAdapter,其界面布局文件item_dish如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_5"
android:background="@drawable/bg_group_button"
android:clickable="true"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tv_dish_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_5"
android:maxLines="1"
android:text="菜名"
android:textColor="@color/decorate_blue"
android:textSize="@dimen/text_size_14" />
<TextView
android:id="@+id/tv_dish_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_5"
android:maxLines="1"
android:text="详情" />
</LinearLayout>
</LinearLayout>
2、同时定义两个ViewHolder
class DishViewHolder extends RecyclerView.ViewHolder {
private TextView tvName;
private TextView tvContent;
public DishViewHolder(View view) {
super(view);
tvName = (TextView) view.findViewById(R.id.tv_dish_name);
tvContent = (TextView) view.findViewById(R.id.tv_dish_content);
}
}
class FootViewHolder extends RecyclerView.ViewHolder{
private TextView tvFootMind;
public FootViewHolder(View view){
super(view);
tvFootMind = (TextView)view.findViewById(R.id.tv_footer_mind);
}
}
3、设置几个重载方法
首先在DishAdapter中定义两个item类别常量
private static final int TYPE_ITEM = 0;//普通item view
private static final int TYPE_FOOTER=1;//底部foot view
@Override
public long getItemId(int position) {
return super.getItemId(position);
}
@Override
public int getItemCount() {
return mDishes.size() + 1;
}
@Override
public int getItemViewType(int position) {
if(position + 1 == getItemCount()){
return TYPE_FOOTER;
}else{
return TYPE_ITEM;
}
}
其中mDishe是定义的DishAdapter数据集合
private List<Dish> mDishes = new ArrayList<>();
4、定义创建和绑定ViewHolder两个重载函数
onCreateViewHolder重载如下
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == TYPE_ITEM){
View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_dish, parent, false);
DishViewHolder dishViewHolder = new DishViewHolder(itemView);
return dishViewHolder;
}else if(viewType == TYPE_FOOTER){
View itemView = LayoutInflater.from(mContext).inflate(R.layout.footer_recycler_load_more,parent,false);
FootViewHolder footViewHolder = new FootViewHolder(itemView);
return footViewHolder;
}
return null;
}
其中footer_recylcer_load_more为自定义的列表上拉加载更多页面布局,其实现为
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_footer_mind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/margin_5"
android:gravity="center"
android:text="上拉加载更多" />
</LinearLayout>
由此可见,onCreateView方法会根据getItemViewType方法返回的列表Item类型来动态的创建对应的ViewHolder。
定义上加载更多的四个状态常量:
private int loadMoreStatus = 0;//0 提示上拉加载 1 正在上拉加载 2 无更多数据 3 隐藏上拉提示
public static final int PULL_UP_LOAD_MORE = 0;
public static final int LOADING_MORE = 1;
public static final int LOAD_END = 2;
public static final int LOAD_GONE = 3;//不可见
到这里便可以定义onBindViewHolder方法了
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof DishViewHolder){
((DishViewHolder)holder).tvName.setText(mDishes.get(position).getName());
((DishViewHolder)holder).tvContent.setText(mDishes.get(position).getContent());
holder.itemView.setTag(position);//没添加这句时ServerDishOnScrollListener没触发
}else if(holder instanceof FootViewHolder){
FootViewHolder footViewHolder = (FootViewHolder)holder;
switch (loadMoreStatus){
case PULL_UP_LOAD_MORE:
footViewHolder.tvFootMind.setText("上拉加载更多...");
break;
case LOADING_MORE:
footViewHolder.tvFootMind.setText("正在加载更多数据...");
break;
case LOAD_END:
footViewHolder.tvFootMind.setText("已无更多数据");
break;
case LOAD_GONE:
footViewHolder.tvFootMind.setVisibility(View.GONE);
break;
}
}
}
onBindViewHolder根据状态来进行不同显示。
5、分页数据加载
先定义几个控制变量
private boolean splitPage = false;//是否分页
private boolean noMoreData = false;//是否有更多数据
private boolean isRefresh = false;//是否是刷新的数据
分页变量
private int pageIndex = 1;//页面序号
private int pageSize = 10;//一次十条数据
加载数据
public void getData(JSONObject jsonObject) {
if(!splitPage){
mDishes.clear();
changeMoreStatus(DishAdapter.LOAD_GONE);
}
if(isRefresh){
mDishes.clear();
isRefresh = false;
pageIndex = 1;
noMoreData = false;
}
if(jsonObject.getIntValue("code") == 0){
JSONArray dishArray = jsonObject.getJSONArray("data");
for (int i = 0; i < dishArray.size(); i++) {
JSONObject json = dishArray.getJSONObject(i);
addData(json);
}
if(splitPage){
if(pageIndex == 1 && dishArray.size() < pageSize) {
changeMoreStatus(DishAdapter.LOAD_GONE);
noMoreData = true;
}else if(dishArray.size() < pageSize){
changeMoreStatus(DishAdapter.LOAD_END);
noMoreData = true;
}else{
++pageIndex;
changeMoreStatus(DishAdapter.PULL_UP_LOAD_MORE);
}
}
}else{
HelpUtil.showToast(mContext,jsonObject.getString("message"));
}
}
然后定义几个控制变量的get set方法,方便外部控制
public boolean getNoMoreData(){
return noMoreData;
}
public int getPageIndex(){
return pageIndex;
}
public int getPageSize(){
return pageSize;
}
public void setRefresh(boolean refresh){
isRefresh = refresh;
pageIndex = 1;
}
public boolean isRefresh() {
return isRefresh;
}
public void changeMoreStatus(int status){
loadMoreStatus = status;
notifyDataSetChanged();
}
到此为止DishAdapter关键点就基本完成。
三、上拉加载更多事件定义
这里利用RecyclerView.OnScrollListener来实现
public class ServerDishOnScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == dishAdapter.getItemCount()) {
if (!dishAdapter.getNoMoreData()) {
dishAdapter.changeMoreStatus(DishAdapter.LOADING_MORE);
getDishData();
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
}
}
其中getDishData为从服务器获取Dish数据方法,获取成功后直接通过dishAdapter.getData(result)即可加载更多数据,当然在使用分页时需要提前设置
this.dishAdapter.setSplitPage(true);
四、下拉刷新事件定义
我们提到了使用SwipeRefreshLayout来实现下拉刷新,那么在定义布局时候请将RecyclerView嵌入该控件中
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl_dish"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_server_dish"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/margin_5" />
</android.support.v4.widget.SwipeRefreshLayout>
然后使用
srlServerDish.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
dishAdapter.setRefresh(true);
getDishData(); //从服务器获取数据
}
});
同时定义刷新完成的动作
@Override
public void stopRefresh() {
srlServerDish.setRefreshing(false);
}
当从服务器获取刷新数据后,判断时候处于刷新状态,如果是的话先停止刷新动作,然后更新数据
if(dishAdapter.isRefresh()){
stopRefresh();
}
dishAdapter.getData(result);
那么下拉刷新的功能就算是完成了。
五、补充说明
1、DishAdatpter getData方法的定义感觉有点乱,如有更好的方法请指点;
2、给一个项目下载地址吧,请参考夏目中的ServerDishActivity页面实现,https://github.com/Yoryky/Diet.git