经验总结-RecyclerView和SwipeRefreshLayout结合实现上拉和下拉刷新

来源:互联网 发布:周思成网络课程百度云 编辑:程序博客网 时间:2024/06/07 13:52

  最近赶工程项目,又用到了RecyclerView,而且结合SwipeRefreshLayout控件实现了下拉刷新。我也算是老Android程序员了,虽然水平还不是很高~~~,以前都是用listview的,什么都用listview,而且感觉很高大上。现在熟悉了RecyclerView后,基本不使用listview了,因为RecyclerView太好用了,而且感觉更神奇,趁着现在有热情,我也正好写一下对这两个控件的总结,并实现RecyclerView的下拉和上拉刷新功能,下面就开始我的总结吧:

一、对RecyclerView和SwipeRefreshLayout的介绍

1.RecyclerView

RecyclerView出来很长时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。

 

你想要控制其显示的方式,请通过布局管理器LayoutManager

你想要控制Item间的间隔(可绘制),请通过ItemDecoration

你想要控制Item增删的动画,请通过ItemAnimator

你想要控制点击、长按事件,请自己写(这个功能需要自己写)

 

相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?

 

那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。


2.SwipeRefreshLayout

SwipeRefrshLayout是Google官方更新的一个Widget,可以实现下拉刷新的效果。该控件集成自ViewGroup在support-v4兼容包下,不过我们需要升级supportlibrary的版本到19.1以上。基本使用的方法如下:

setOnRefreshListener(OnRefreshListener):添加下拉刷新监听器

setRefreshing(boolean):显示或者隐藏刷新进度条

isRefreshing():检查是否处于刷新状态

setColorSchemeResources():设置进度条的颜色主题,最多设置四种,以前的setColorScheme()方法已经弃用了,具体使用效果下面我们会看到。

二.RecyclerView结合SwipeRefrshLayout使用实现下拉和上拉刷新

首先做好准备工作,配置好build.gradle,添加依赖,

dependencies {

...

compile 'com.android.support:appcompat-v7:25.3.1'

compile 'com.android.support:recyclerview-v7:25.+'

...

}

1.下拉

xml  activity_main

<?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="match_parent"    android:orientation="vertical">    <android.support.v4.widget.SwipeRefreshLayout        android:id="@+id/swipe_refresh_layout"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:scrollbars="vertical"        >        <android.support.v7.widget.RecyclerView            android:id="@+id/recycler_view"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            ></android.support.v7.widget.RecyclerView>    </android.support.v4.widget.SwipeRefreshLayout>    </LinearLayout>
xml    item
<?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="match_parent"    android:orientation="vertical">    <TextView        android:id="@+id/tv_item"        android:layout_width="match_parent"        android:layout_height="50dp"        android:gravity="center"        android:text="1" /></LinearLayout>

java activity

package com.xgr.refreshdemo.ui;import android.os.Bundle;import android.os.Handler;import android.support.v4.widget.SwipeRefreshLayout;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.OrientationHelper;import android.support.v7.widget.RecyclerView;import android.util.Log;import android.util.TypedValue;import android.widget.Toast;import com.xgr.refreshdemo.R;import com.xgr.refreshdemo.adapter.RefreshRecyclerAdapter;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private SwipeRefreshLayout swiperefreshlayout;    private RecyclerView recyclerView;    private RefreshRecyclerAdapter adapter;    private LinearLayoutManager linearLayoutManager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                setViews();        initData();            }        private void setViews() {        swiperefreshlayout = (SwipeRefreshLayout) this.findViewById(R.id.swipe_refresh_layout);        recyclerView = (RecyclerView) this.findViewById(R.id.recycler_view);     }    private void initData() {        //设置刷新时动画的颜色,可以设置4个        swiperefreshlayout.setProgressBackgroundColorSchemeResource(android.R.color.white);        swiperefreshlayout.setColorSchemeResources(android.R.color.holo_blue_light,                android.R.color.holo_red_light, android.R.color.holo_orange_light,                android.R.color.holo_green_light);        swiperefreshlayout.setProgressViewOffset(false, 0, (int) TypedValue                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()                        .getDisplayMetrics()));        linearLayoutManager = new LinearLayoutManager(this);        linearLayoutManager.setOrientation(OrientationHelper.VERTICAL);        recyclerView.setLayoutManager(linearLayoutManager);        //添加分隔线        recyclerView.setAdapter(adapter = new RefreshRecyclerAdapter(this));        swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                Log.d("zttjiangqq", "invoke onRefresh...");                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        List<String> newDatas = new ArrayList<String>();                        for (int i = 0; i < 5; i++) {                            int index = i + 1;                            newDatas.add("new item" + index);                        }                        adapter.addItem(newDatas);                        swiperefreshlayout.setRefreshing(false);                        Toast.makeText(MainActivity.this, "更新了五条数据...", Toast.LENGTH_SHORT).show();                    }                }, 5000);            }        });    }}

java adapter

package com.xgr.refreshdemo.adapter;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import com.xgr.refreshdemo.R;import java.util.ArrayList;import java.util.List;/** * Created by Administrator on 2017/10/31. */public class RefreshRecyclerAdapter extends RecyclerView.Adapter<RefreshRecyclerAdapter.ViewHolder>{    private LayoutInflater mInflater;    private List<String> mTitles=null;    public RefreshRecyclerAdapter(Context context){        this.mInflater=LayoutInflater.from(context);        this.mTitles=new ArrayList<String>();        for (int i=0;i<20;i++){            int index=i+1;            mTitles.add("item"+index);        }    }    /**     * item显示类型     * @param parent     * @param viewType     * @return     */    @Override    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        final View view=mInflater.inflate(R.layout.item_recycler_layout,parent,false);        //这边可以做一些属性设置,甚至事件监听绑定        //view.setBackgroundColor(Color.RED);        ViewHolder viewHolder=new ViewHolder(view);        return viewHolder;    }    /**     * 数据的绑定显示     * @param holder     * @param position     */    @Override    public void onBindViewHolder(ViewHolder holder, int position) {        holder.item_tv.setText(mTitles.get(position));        holder.itemView.setTag(position);    }    @Override    public int getItemCount() {        return mTitles.size();    }    //自定义的ViewHolder,持有每个Item的的所有界面元素    public static class ViewHolder extends RecyclerView.ViewHolder {        public TextView item_tv;        public ViewHolder(View view){            super(view);            item_tv = (TextView)view.findViewById(R.id.tv_item);        }    }    //添加数据    public void addItem(List<String> newDatas) {        //mTitles.add(position, data);        //notifyItemInserted(position);        newDatas.addAll(mTitles);        mTitles.removeAll(mTitles);        mTitles.addAll(newDatas);        notifyDataSetChanged();    }    public void addMoreItem(List<String> newDatas) {        mTitles.addAll(newDatas);        notifyDataSetChanged();    }}

以上重要地方的注释已经加上去了。


2.上拉刷新

(1)RecyclerView设置滚动事件加入上拉加载更多功能

下面我们再来看RecyclerView和相关类的一些特性,

LayoutManger给我们提供了以下几个方法来让开发者方便的获取到屏幕上面的顶部item和顶部item相关的信息:

findFirstVisibleItemPosition()

findFirstCompletlyVisibleItemPosition()

findLastVisibleItemPosition()

findLastCompletlyVisibleItemPosition()

  同时通过Recycler.Adapter的getItemCount()方法可以轻松获取到RecyclerView列表中Item View的个数。

那么下面我们通过监听滑动(滚动)事件,然后在里边判断是否已经滑动到最底部来加载更多的数据,使用方法如下:

//RecyclerView滑动监听        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {            @Override            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                super.onScrollStateChanged(recyclerView, newState);                //此处声明了一个int类型成员变量lastVisibleItem                if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {                    new Handler().postDelayed(new Runnable() {                        @Override                        public void run() {                            List<String> newDatas = new ArrayList<String>();                            for (int i = 0; i< 5; i++) {                                int index = i +1;                                newDatas.add("more item" + index);                            }                            adapter.addMoreItem(newDatas);                        }                    },1000);                }            }            @Override            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {                super.onScrolled(recyclerView,dx, dy);                lastVisibleItem =linearLayoutManager.findLastVisibleItemPosition();            }        });

(2)RecyclerView加入FootView实现上拉加载

  上面我们虽然已经实现了上拉加载更多的效果,但是还比较丑陋,最起码要让用户知道确实在上拉加载的过程吧,例如加载一个底部的进度布局。这样一想,那么我们就按照ListView方式addFootView()呗,不过很可惜的是RecyclerView没有给我们提供addFootView()方法,那该怎么样办呢?我们来看RecyclerView.Apapter类:

我们要实现一个自定义Adapter一定需要实现onCreateViewHolder(ViewGroup paren,int viewType)方法,注意看方法中的第二个参数viewType,是不是想到布局类型了,也就是说该也支持多套布局显示的,那么查看基类中的所有方法如下:



上面有一个方法getItemType(),这个就和ListView的Adapter的实现差不多了,那么我们这边可以使用多套布局给RecyclerView加入一个FootView布局即可。RefreshFootAdapter.java具体实现流程如下:

1.加入布局状态标志-用来判断此时加载是普通Item还是foot view:

   private static final int TYPE_ITEM =0;  //普通Item View

   private static final intTYPE_FOOTER = 1;  //底部FootView

2.重写getItemCount()方法,返回的Item数量在数据的基础上面+1,增加一项FootView布局项

public intgetItemCount() {       return mTitles.size()+1;   }  

3.重写getItemViewType方法来判断返回加载的布局的类型   

public int getItemViewType(int position) {    // 最后一个item设置为footerView    if (position + 1 == getItemCount()) {                return TYPE_FOOTER;            } else {                return TYPE_ITEM;            }        }  

4.接着onCreateViewHolder(ViewGroup parent,int viewType)加载布局的时候根据viewType的类型来选择指定的布局创建,返回即可:

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {          //进行判断显示类型,来创建返回不同的View          if(viewType==TYPE_ITEM){              Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);              //这边可以做一些属性设置,甚至事件监听绑定             //view.setBackgroundColor(Color.RED);              ItemViewHolder itemViewHolder=new ItemViewHolder(view);              return itemViewHolder;          }else if(viewType==TYPE_FOOTER){              Viewfoot_view=mInflater.inflate(R.layout.recycler_load_more_layout,parent,false);              //这边可以做一些属性设置,甚至事件监听绑定             //view.setBackgroundColor(Color.RED);              FootViewHolder footViewHolder=new FootViewHolder(foot_view);              return footViewHolder;          }         return null;      }  

5.最后进行判断数据的时候(onBindViewHolder),判断holder的类型来进行判定数据即可.

所有代码如下:

packagecom.chinaztt.fda.adapter;  public class RefreshFootAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{      //上拉加载更多      public static final int  PULLUP_LOAD_MORE=0;      //正在加载中      public static final int  LOADING_MORE=1;      //上拉加载更多状态-默认为0      private int load_more_status=0;      private LayoutInflater mInflater;      private List<String> mTitles=null;      private static final intTYPE_ITEM = 0;  //普通Item View      private static final intTYPE_FOOTER = 1;  //顶部FootView      public RefreshFootAdapter(Context context){         this.mInflater=LayoutInflater.from(context);          this.mTitles=new ArrayList<String>();          for (int i=0;i<20;i++){              int index=i+1;             mTitles.add("item"+index);          }      }      /**      * item显示类型      * @param parent      * @param viewType      * @return      */      public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {          //进行判断显示类型,来创建返回不同的View          if(viewType==TYPE_ITEM){              Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);              //这边可以做一些属性设置,甚至事件监听绑定             //view.setBackgroundColor(Color.RED);              ItemViewHolder itemViewHolder=new ItemViewHolder(view);              return itemViewHolder;          }else if(viewType==TYPE_FOOTER){              Viewfoot_view=mInflater.inflate(R.layout.recycler_load_more_layout,parent,false);              //这边可以做一些属性设置,甚至事件监听绑定             //view.setBackgroundColor(Color.RED);              FootViewHolder footViewHolder=new FootViewHolder(foot_view);              return footViewHolder;          }         return null;      }         /**      * 数据的绑定显示      * @param holder      * @param position      */      public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {          if(holder instanceof ItemViewHolder) {             ((ItemViewHolder)holder).item_tv.setText(mTitles.get(position));              holder.itemView.setTag(position);          }else if(holder instanceof FootViewHolder){              FootViewHolder footViewHolder=(FootViewHolder)holder;              switch (load_more_status){                  case PULLUP_LOAD_MORE:                     footViewHolder.foot_view_item_tv.setText("上拉加载更多...");                      break;                  case LOADING_MORE:                     footViewHolder.foot_view_item_tv.setText("正在加载更多数据...");                      break;              }          }      }         /**      * 进行判断是普通Item视图还是FootView视图      * @param position      * @return      */      @Override      public int getItemViewType(int position) {      // 最后一个item设置为footerView      if (position + 1 == getItemCount()) {                  return TYPE_FOOTER;              } else {                  return TYPE_ITEM;              }          }      @Override      public int getItemCount() {          return mTitles.size()+1;      }      //自定义的ViewHolder,持有每个Item的的所有界面元素      public static class ItemViewHolder extends RecyclerView.ViewHolder {          public TextView item_tv;          public ItemViewHolder(View view){              super(view);              item_tv = (TextView)view.findViewById(R.id.item_tv);          }      }      /**      * 底部FootView布局      */      public static class FootViewHolder extends  RecyclerView.ViewHolder{          private TextView foot_view_item_tv;          public FootViewHolder(View view) {              super(view);             foot_view_item_tv=(TextView)view.findViewById(R.id.foot_view_item_tv);          }      }         //添加数据      public void addItem(List<String> newDatas) {          //mTitles.add(position, data);          //notifyItemInserted(position);          newDatas.addAll(mTitles);          mTitles.removeAll(mTitles);          mTitles.addAll(newDatas);          notifyDataSetChanged();      }         public void addMoreItem(List<String> newDatas) {          mTitles.addAll(newDatas);          notifyDataSetChanged();      }         /**      * //上拉加载更多      * PULLUP_LOAD_MORE=0;      * //正在加载中      * LOADING_MORE=1;      * //加载完成已经没有更多数据了      * NO_MORE_DATA=2;      * @param status      */      public void changeMoreStatus(int status){          load_more_status=status;          notifyDataSetChanged();      }  }  

同时该Adaper中我还定义一个changeMoreStatus()方法和两个字符串常量可以来进行修改FootView中字符串提醒文本的。


7.整体Activity中还是设置监听RecyclerView的滚动事件.代码和第一个例子差不多,不过RecyclerView需要设置这边的Adapter,demo_recycler.setAdapter(adapter= new RefreshFootAdapter(this));

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {              @Override              public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                 super.onScrollStateChanged(recyclerView, newState);                  if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {                     adapter.changeMoreStatus(RefreshFootAdapter.LOADING_MORE);                      newHandler().postDelayed(new Runnable() {                          @Override                          public void run() {                              List<String> newDatas = new ArrayList<String>();                              for (int i = 0; i< 5; i++) {                                  int index = i +1;                                 newDatas.add("more item" + index);                              }                             adapter.addMoreItem(newDatas);                             adapter.changeMoreStatus(RefreshFootAdapter.PULLUP_LOAD_MORE);                          }                      }, 2500);                  }              }              @Override              public void onScrolled(RecyclerView recyclerView, int dx, int dy) {                  super.onScrolled(recyclerView,dx, dy);                  lastVisibleItem =linearLayoutManager.findLastVisibleItemPosition();              }          });  

  查看代码之后,大家肯定也发现在onScrollStateChanged()方法中,Adapter.addMoreItem()和Adapter.changeMoreStatus()方法内部都调用了notifyDataSetChanged()方法,也就是说这边刷新了两次,针对这个问题,大家可以把第二个方法放入到addMoreItem()内部去,调用一次刷新操作接口。同时也可以自己按照实际需求进行优化。下拉刷新和上拉刷新我是分开来写的,如果大家觉得一个页面里面同时有下拉刷新和上拉刷新功能更好些,就把上拉刷新的代码整合到下拉刷新的adapter中去。

  今天就到这里了,大家如果有什么好的建议可以给我留言

 


阅读全文
0 0
原创粉丝点击