Android开发之SwipeRefreshLayout详解和自定义加载更多

转载 2017年01月03日 14:11:50

本文重点介绍了SwipeRefreshLayout的使用和自定View继承SwipeRefreshLayout添加上拉加载更多的功能。

:  

 

      话说RecyclerView已经面市很久,也在很多应用中得到广泛的使用,在整个开发者圈子里面也拥有很不错的口碑,那说明RecyclerView拥有比ListView,GridView之类控件有很多的优点,例如:数据绑定,Item View创建,View的回收以及重用等机制。前三三篇文章已经贡呢更新了以下三个部分:

  1. RecyclerView控件的基本使用,包括基础,进阶,高级部分,动画之类(点击进入)
  2. RecyclerView控件的实战实例(点击进入)
  3. RecyclerView控件集合AA(Android Annotations)注入框架实例(点击进入)

         本来这个专题不打算更新,不过前两天看到各位童鞋还是挺积极的评论到,希望可以更新RecyclerView加入下拉刷新和上拉加载更多的功能。正好昨天周末,所以我这边也就实现了这样的功能,今天更新一下。具体代码已经上传到下面的项目中,欢迎各位去star和fork一下。

         FastDev4Android框架项目地址:https://github.com/jiangqqlmj/FastDev4Android

         RecyclerView实现的列表,默认情况下面是不带下拉刷新和上拉记载更多效果的,但是我在我们的实际项目当中,为了提高用户体验,这种效果一般都需要实现,在我以前的博客中已经重写了ListView(Android 列表下拉刷新组件PullToRefreshListView使用)实现上拉刷新和上拉加载更多效果。现在我们已经学会了ListView,GridView的替代品RecyclerView的基本使用方法,那么必不可少的也需要实现上拉刷新和上拉加载更多的效果了。今天我们会通过两种方式来实现,具体会采用Android的另一控件SwipeRefreshLayout

SwipeRefreshLayout介绍:

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

  • setOnRefreshListener(OnRefreshListener):添加下拉刷新监听器
  • setRefreshing(boolean):显示或者隐藏刷新进度条
  • isRefreshing():检查是否处于刷新状态
  • setColorSchemeResources():设置进度条的颜色主题,最多设置四种,以前的setColorScheme()方法已经弃用了。

     具体使用效果下面我们会看到。


实现演示:---------(一)

  • 介绍之前,先来看一下SwipeRefreshLayout实现的下拉刷新效果图。从图中可以看到,下拉到了一定的高度才会进行刷新,高度不够就会回收上去,正在刷新过程中,继续下拉没反应,说明刷新时屏蔽掉了下拉事件。 
    上拉刷新效果图

一、SwipeRefreshLayout简单介绍

  • 先看以下官方文档,已有了很详细的描述了。 
    官方文档说明

  • 这里我再大概解释一下:

    • 在竖直滑动时想要刷新页面可以用SwipeRefreshLayout来实现。它通过设置OnRefreshListener来监听界面的滑动从而实现刷新。也可以通过一些方法来设置SwipeRefreshLayout是否可以刷新。如:setRefreshing(true),展开刷新动画。 
      setRefreshing(false),取消刷新动画。setEnable(true)下拉刷新将不可用。

    • 使用这个布局要想达到刷新的目的,需要在这个布局里包裹可以滑动的子控件,如ListView等,并且只能有一个子控件。

  • 介绍总结:使用SwipeRefreshLayout可以实现下拉刷新,前提是布局里需要包裹一个可以滑动的子控件,然后在代码里设置OnRefreshListener设置监听,最后在监听里设置刷新时的数据获取就可以了。由于是新出来的东西,所以要想使用,先把support library的版本升级到19.1或更新。

二、SwipeRefreshLayout主要方法介绍

翻看官方的文档,可以看到方法有很多,这里只介绍五个经常用到的方法。

  • isRefreshing()

    • 判断当前的状态是否是刷新状态。
  • setColorSchemeResources(int… colorResIds)

    • 设置下拉进度条的颜色主题,参数为可变参数,并且是资源id,最多可以设置四种不同的颜色,每转一圈就显示一种颜色。
  • setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener)

    • 设置监听,需要重写onRefresh()方法,顶部下拉时会调用这个方法,在里面实现请求数据的逻辑,设置下拉进度条消失等等。
  • setProgressBackgroundColorSchemeResource(int colorRes)

    • 设置下拉进度条的背景颜色,默认白色。
  • setRefreshing(boolean refreshing)

    • 设置刷新状态,true表示正在刷新,false表示取消刷新。

三、SwipeRefreshLayout的基本使用

  • 介绍了SwipeRefreshLayout,主要的方法也讲了,接下来就是实战,其实使用起来非常的简单。

3.1 设置布局

  • 官方文档已经说明,SwipeRefreshLayout只能有一个孩子,当然我们不般也不会往里面放其他的布局。我们只需要在容器里包裹一个ListView就好了。

    <!--使用谷歌官方的下拉刷新组件,只有下拉刷新功能-->
    <!--
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/srl"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <ListView
                android:id="@+id/lv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    
        </android.support.v4.widget.SwipeRefreshLayout>
    -->
    

3.2 在代码中使用

  • 在该布局文件对应的Activity或其他类中获取布局id,先设置ListView显示的适配器,然后再设置SwipeRefreshLayout。

    // 不能在onCreate中设置,这个表示当前是刷新状态,如果一进来就是刷新状态,SwipeRefreshLayout会屏蔽掉下拉事件
    //swipeRefreshLayout.setRefreshing(true);
    
    // 设置颜色属性的时候一定要注意是引用了资源文件还是直接设置16进制的颜色,因为都是int值容易搞混
    // 设置下拉进度的背景颜色,默认就是白色的
    swipeRefreshView.setProgressBackgroundColorSchemeResource(android.R.color.white);
    // 设置下拉进度的主题颜色
    swipeRefreshView.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark);
    
    // 下拉时触发SwipeRefreshLayout的下拉动画,动画完毕之后就会回调这个方法
    swipeRefreshView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
    
            // 开始刷新,设置当前为刷新状态
            //swipeRefreshLayout.setRefreshing(true);
    
            // 这里是主线程
            // 一些比较耗时的操作,比如联网获取数据,需要放到子线程去执行
            // TODO 获取数据
            final Random random = new Random();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mList.add(0, "我是天才" + random.nextInt(100) + "号");
                    mAdapter.notifyDataSetChanged();
    
                    Toast.makeText(MainActivity.this, "刷新了一条数据", Toast.LENGTH_SHORT).show();
    
                    // 加载完数据设置为不刷新状态,将下拉进度收起来
                    swipeRefreshView.setRefreshing(false);
                }
            }, 1200);
    
            // System.out.println(Thread.currentThread().getName());
    
            // 这个不能写在外边,不然会直接收起来
            //swipeRefreshLayout.setRefreshing(false);
        }
    });
    
  • 经过以上两步简单的设置就能使用SwipeRefreshLayout了。

四、自定义View继承SwipeRefreshLayout,添加上拉加载更多功能

由于谷歌并没有提供上拉加载更多的布局,所以我们只能自己去定义布局实现这个功能。

这里通过自定义View继承SwipeRefreshLayout容器,然后添加上拉加载更多的功能。

  • 先来看一下上拉加载更多的效果图 
    上拉加载更多效果图

4.1 定义View继承SwipeRefreshLayout,添加上拉加载功能

代码中的注释比较详细,这里就不一一解释了,说一下大概的实现思路,主要分为四步。

4.1.1 获取子控件ListView

  • 在布局使用中,这里和SwipeRefreshLayout一样,ListView是SwipeRefreshView的子控件,所以需要在onLayout()方法中获取子控件ListView。

    // 获取ListView,设置ListView的布局位置
    if (mListView == null) {
        // 判断容器有多少个孩子
        if (getChildCount() > 0) {
            // 判断第一个孩子是不是ListView
            if (getChildAt(0) instanceof ListView) {
                // 创建ListView对象
                mListView = (ListView) getChildAt(0);
    
                // 设置ListView的滑动监听
                setListViewOnScroll();
            }
        }
    }
    

4.1.2 对ListView设置滑动监听

  • 监听ListView的滑动事件,当滑动到底部,并且当前可见页的最后一个条目等于adapter的getCount数目-1,就满足加载数据的条件。

       /**
         * 设置ListView的滑动监听
         */
        private void setListViewOnScroll() {
    
            mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    // 移动过程中判断时候能下拉加载更多
                    if (canLoadMore()) {
                        // 加载数据
                        loadData();
                    }
                }
    
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    
                }
            });
        }
    

4.1.3 处理SwipeRefreshView容器的分发事件

  • 由于ListView是SwipeRefreshView的子控件,所以这里要进行事件的分发处理,判断用户的滑动距离是否满足条件。

     /**
     * 在分发事件的时候处理子控件的触摸事件
     *
     * @param ev
     * @return
     */
    private float mDownY, mUpY;
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 移动的起点
                mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 移动过程中判断时候能下拉加载更多
                if (canLoadMore()) {
                    // 加载数据
                    loadData();
                }
    
                break;
            case MotionEvent.ACTION_UP:
                // 移动的终点
                mUpY = getY();
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    

4.1.4 判断条件,满足就用回调去加载数据

  • 当满足了需要判断的所有的条件之后,就可以去调用加载数据的方法,这里提供一个设置上拉布局显示和隐藏的方法,通过传入当前的状态,是true就显示加载,是false就隐藏。

    /**
     * 判断是否满足加载更多条件
     *
     * @return
     */
    private boolean canLoadMore() {
        // 1. 是上拉状态
        boolean condition1 = (mDownY - mUpY) >= mScaledTouchSlop;
        if (condition1) {
            System.out.println("是上拉状态");
        }
    
        // 2. 当前页面可见的item是最后一个条目
        boolean condition2 = false;
        if (mListView != null && mListView.getAdapter() != null) {
            condition2 = mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
        }
    
        if (condition2) {
            System.out.println("是最后一个条目");
        }
        // 3. 正在加载状态
        boolean condition3 = !isLoading;
        if (condition3) {
            System.out.println("不是正在加载状态");
        }
        return condition1 && condition2 && condition3;
    }
    
    /**
     * 处理加载数据的逻辑
     */
    private void loadData() {
        System.out.println("加载数据...");
        if (mOnLoadListener != null) {
            // 设置加载状态,让布局显示出来
            setLoading(true);
            mOnLoadListener.onLoad();
        }
    
    }
    
    /**
     * 设置加载状态,是否加载传入boolean值进行判断
     *
     * @param loading
     */
    public void setLoading(boolean loading) {
        // 修改当前的状态
        isLoading = loading;
        if (isLoading) {
            // 显示布局
            mListView.addFooterView(mFooterView);
        } else {
            // 隐藏布局
            mListView.removeFooterView(mFooterView);
    
            // 重置滑动的坐标
            mDownY = 0;
            mUpY = 0;
        }
    }
    

4.2 使用自定义View

4.2.1. 书写布局

  • 因为是继承自SwipeRefreshLayout,所以SwipeRefreshView也只能有一个孩子

    <!--自定义View实现SwipeRefreshLayout,添加上拉加载更多的功能-->
    <com.pinger.swiperefreshdemo.view.SwipeRefreshView
        android:id="@+id/srl"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ListView
            android:id="@+id/lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </com.pinger.swiperefreshdemo.view.SwipeRefreshView>
    

4.2.2. 在代码中使用

  • 在代码中使用更加的简单,只需要设置监听重写onLoad()方法,在里面加载数据,加载完数据然后设置为不加载状态就可以了。

    // 设置下拉加载更多
    swipeRefreshView.setOnLoadListener(new SwipeRefreshView.OnLoadListener() {
        @Override
        public void onLoad() {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
    
                    // 添加数据
                    for (int i = 30; i < 35; i++) {
                        mList.add("我是天才" + i+ "号");
                        // 这里要放在里面刷新,放在外面会导致刷新的进度条卡住
                        mAdapter.notifyDataSetChanged();
                    }
    
                    Toast.makeText(MainActivity.this, "加载了" + 5 + "条数据", Toast.LENGTH_SHORT).show();
    
                    // 加载完数据设置为不加载状态,将加载进度收起来
                    swipeRefreshView.setLoading(false);
                }
            }, 1200);
        }
    });
    


实现演示(二)

(1).RecyclerView+SwpieRefreshLayout实现下拉刷新效果:

       1.SwipeRefreshLayout本身自带下拉刷新的效果,那么我们可以选择在RecyclerView布局外部嵌套一层SwipeRefreshLayout布局即可,具体布局文件如下:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xmlversionxmlversion="1.0" encoding="utf-8"?>  
  2. <LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"android:layout_width="match_parent"  
  4.    android:layout_height="match_parent">  
  5.     <includelayoutincludelayout="@layout/common_top_bar_layout"/>  
  6.    <android.support.v4.widget.SwipeRefreshLayout  
  7.        android:id="@+id/demo_swiperefreshlayout"  
  8.        android:layout_width="fill_parent"  
  9.        android:layout_height="fill_parent"  
  10.         android:scrollbars="vertical"  
  11.         >  
  12.        <android.support.v7.widget.RecyclerView  
  13.            android:id="@+id/demo_recycler"  
  14.            android:layout_width="fill_parent"  
  15.            android:layout_height="fill_parent"  
  16.            ></android.support.v7.widget.RecyclerView>  
  17.    </android.support.v4.widget.SwipeRefreshLayout>  
  18. </LinearLayout>  

      2.接着在Activity中获取SwipeRefreshLayout控件并且设置OnRefreshListener监听器,同时实现里边的onRefresh()方法,在该方法中进行网络请求最新数据,然后刷新RecyclerView列表同时设置SwipeRefreshLayout的进度Bar的隐藏或者显示效果。具体代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. demo_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {  
  2.             @Override  
  3.             public void onRefresh() {  
  4.                 Log.d("zttjiangqq","invoke onRefresh...");  
  5.                 new Handler().postDelayed(newRunnable() {  
  6.                     @Override  
  7.                     public void run() {  
  8.                         List<String> newDatas = new ArrayList<String>();  
  9.                         for (int i = 0; i <5; i++) {  
  10.                             int index = i + 1;  
  11.                            newDatas.add("new item" + index);  
  12.                         }  
  13.                        adapter.addItem(newDatas);  
  14.                        demo_swiperefreshlayout.setRefreshing(false);  
  15.                        Toast.makeText(RecyclerRefreshActivity.this"更新了五条数据...", Toast.LENGTH_SHORT).show();  
  16.                     }  
  17.                 }, 5000);  
  18.             }  
  19.         });  

   3.除此之外我们也来看一下AdapterActivity中的其他代码,也方便各位童鞋查看。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. RecyclerRefreshActivity.java  
  2. public class RecyclerRefreshActivity extends BaseActivity {  
  3.     private LinearLayout top_bar_linear_back;  
  4.     private TextView top_bar_title;  
  5.     private SwipeRefreshLayout demo_swiperefreshlayout;  
  6.     private RecyclerView demo_recycler;  
  7.     private RefreshRecyclerAdapter adapter;  
  8.     private LinearLayoutManager linearLayoutManager;  
  9.     private int lastVisibleItem;  
  10.     @Override  
  11.     protected void onCreate(BundlesavedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.        setContentView(R.layout.recycler_refresh_layout);  
  14.        top_bar_linear_back=(LinearLayout)this.findViewById(R.id.top_bar_linear_back);  
  15.        top_bar_linear_back.setOnClickListener(new CustomOnClickListener());  
  16.        top_bar_title=(TextView)this.findViewById(R.id.top_bar_title);  
  17.        top_bar_title.setText("RecyclerView下拉刷新,下拉加载更多...");  
  18.        demo_swiperefreshlayout=(SwipeRefreshLayout)this.findViewById(R.id.demo_swiperefreshlayout);  
  19.        demo_recycler=(RecyclerView)this.findViewById(R.id.demo_recycler);  
  20.         //设置刷新时动画的颜色,可以设置4个  
  21.        demo_swiperefreshlayout.setProgressBackgroundColorSchemeResource(android.R.color.white);  
  22.        demo_swiperefreshlayout.setColorSchemeResources(android.R.color.holo_blue_light,  
  23.                 android.R.color.holo_red_light,android.R.color.holo_orange_light,  
  24.                android.R.color.holo_green_light);  
  25.        demo_swiperefreshlayout.setProgressViewOffset(false0, (int) TypedValue  
  26.                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()  
  27.                         .getDisplayMetrics()));  
  28.         linearLayoutManager=new LinearLayoutManager(this);  
  29.        linearLayoutManager.setOrientation(OrientationHelper.VERTICAL);  
  30.        demo_recycler.setLayoutManager(linearLayoutManager);  
  31.         //添加分隔线  
  32.         demo_recycler.addItemDecoration(new AdvanceDecoration(this, OrientationHelper.VERTICAL));  
  33.         demo_recycler.setAdapter(adapter = new RefreshRecyclerAdapter(this));  
  34.        demo_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {  
  35.             @Override  
  36.             public void onRefresh() {  
  37.                 Log.d("zttjiangqq","invoke onRefresh...");  
  38.                 new Handler().postDelayed(newRunnable() {  
  39.                     @Override  
  40.                     public void run() {  
  41.                         List<String> newDatas = new ArrayList<String>();  
  42.                         for (int i = 0; i <5; i++) {  
  43.                             int index = i + 1;  
  44.                            newDatas.add("new item" + index);  
  45.                         }  
  46.                        adapter.addItem(newDatas);  
  47.                        demo_swiperefreshlayout.setRefreshing(false);  
  48.                        Toast.makeText(RecyclerRefreshActivity.this"更新了五条数据...", Toast.LENGTH_SHORT).show();  
  49.                     }  
  50.                 }, 5000);  
  51.             }  
  52.         });  
  53.           class CustomOnClickListenerimplements View.OnClickListener{  
  54.         @Override  
  55.         public void onClick(View v) {  
  56.            RecyclerRefreshActivity.this.finish();  
  57.         }  
  58.     }  
  59. }  

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. RefreshRecyclerAdapter.java  
  2. public class RefreshRecyclerAdapter extends RecyclerView.Adapter<RefreshRecyclerAdapter.ViewHolder>{  
  3.     private LayoutInflater mInflater;  
  4.     private List<String> mTitles=null;  
  5.     public RefreshRecyclerAdapter(Context context){  
  6.        this.mInflater=LayoutInflater.from(context);  
  7.         this.mTitles=new ArrayList<String>();  
  8.         for (int i=0;i<20;i++){  
  9.             int index=i+1;  
  10.            mTitles.add("item"+index);  
  11.         }  
  12.     }  
  13.     /** 
  14.      * item显示类型 
  15.      * @param parent 
  16.      * @param viewType 
  17.      * @return 
  18.      */  
  19.     @Override  
  20.     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  21.         final Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);  
  22.         //这边可以做一些属性设置,甚至事件监听绑定  
  23.         //view.setBackgroundColor(Color.RED);  
  24.         ViewHolder viewHolder=new ViewHolder(view);  
  25.    
  26.         return viewHolder;  
  27.     }  
  28.    
  29.     /** 
  30.      * 数据的绑定显示 
  31.      * @param holder 
  32.      * @param position 
  33.      */  
  34.     @Override  
  35.     public void onBindViewHolder(ViewHolder holder, int position) {  
  36.        holder.item_tv.setText(mTitles.get(position));  
  37.         holder.itemView.setTag(position);  
  38.     }  
  39.     @Override  
  40.     public int getItemCount() {  
  41.         return mTitles.size();  
  42.     }  
  43.    
  44.     //自定义的ViewHolder,持有每个Item的的所有界面元素  
  45.     public static class ViewHolder extends RecyclerView.ViewHolder {  
  46.         public TextView item_tv;  
  47.         public ViewHolder(View view){  
  48.             super(view);  
  49.             item_tv = (TextView)view.findViewById(R.id.item_tv);  
  50.         }  
  51.     }  
  52.    
  53.     //添加数据  
  54.     public void addItem(List<String> newDatas) {  
  55.         //mTitles.add(position, data);  
  56.         //notifyItemInserted(position);  
  57.         newDatas.addAll(mTitles);  
  58.         mTitles.removeAll(mTitles);  
  59.         mTitles.addAll(newDatas);  
  60.         notifyDataSetChanged();  
  61.     }  
  62.    
  63.     public void addMoreItem(List<String> newDatas) {  
  64.         mTitles.addAll(newDatas);  
  65.         notifyDataSetChanged();  
  66.     }  
  67. }  

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

5.运行效果如下:


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

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

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

  • findFirstVisibleItemPosition()
  • findFirstCompletlyVisibleItemPosition()
  • findLastVisibleItemPosition()
  • findLastCompletlyVisibleItemPosition()

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

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //RecyclerView滑动监听  
  2.        demo_recycler.setOnScrollListener(new RecyclerView.OnScrollListener() {  
  3.            @Override  
  4.            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  
  5.               super.onScrollStateChanged(recyclerView, newState);  
  6.                if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {  
  7.                    new Handler().postDelayed(new Runnable() {  
  8.                        @Override  
  9.                        public void run() {  
  10.                            List<String> newDatas = new ArrayList<String>();  
  11.                            for (int i = 0; i< 5; i++) {  
  12.                                int index = i +1;  
  13.                               newDatas.add("more item" + index);  
  14.                            }  
  15.                           adapter.addMoreItem(newDatas);  
  16.                        }  
  17.                    },1000);  
  18.                }  
  19.            }  
  20.            @Override  
  21.            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {  
  22.                super.onScrolled(recyclerView,dx, dy);  
  23.                lastVisibleItem =linearLayoutManager.findLastVisibleItemPosition();  
  24.            }  
  25.        });  

运行效果如下:


(3).升级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布局项

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public intgetItemCount() {  
  2.      return mTitles.size()+1;  
  3.  }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public int getItemViewType(int position) {  
  2.   // 最后一个item设置为footerView  
  3.   if (position + 1 == getItemCount()) {  
  4.               return TYPE_FOOTER;  
  5.           } else {  
  6.               return TYPE_ITEM;  
  7.           }  
  8.       }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  2.         //进行判断显示类型,来创建返回不同的View  
  3.         if(viewType==TYPE_ITEM){  
  4.             Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);  
  5.             //这边可以做一些属性设置,甚至事件监听绑定  
  6.            //view.setBackgroundColor(Color.RED);  
  7.             ItemViewHolder itemViewHolder=new ItemViewHolder(view);  
  8.             return itemViewHolder;  
  9.         }else if(viewType==TYPE_FOOTER){  
  10.             Viewfoot_view=mInflater.inflate(R.layout.recycler_load_more_layout,parent,false);  
  11.             //这边可以做一些属性设置,甚至事件监听绑定  
  12.            //view.setBackgroundColor(Color.RED);  
  13.             FootViewHolder footViewHolder=new FootViewHolder(foot_view);  
  14.             return footViewHolder;  
  15.         }  
  16.        return null;  
  17.     }  

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  2.        if(holder instanceof ItemViewHolder) {  
  3.           ((ItemViewHolder)holder).item_tv.setText(mTitles.get(position));  
  4.            holder.itemView.setTag(position);  
  5.        }else if(holder instanceof FootViewHolder){  
  6.            FootViewHolderfootViewHolder=(FootViewHolder)holder;  
  7.            switch (load_more_status){  
  8.                case PULLUP_LOAD_MORE:  
  9.                   footViewHolder.foot_view_item_tv.setText("上拉加载更多...");  
  10.                    break;  
  11.                case LOADING_MORE:  
  12.                   footViewHolder.foot_view_item_tv.setText("正在加载更多数据...");  
  13.                    break;  
  14.            }  
  15.        }  
  16.    }  
6.整个RefreshFootAdapter完整代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. packagecom.chinaztt.fda.adapter;  
  2. public class RefreshFootAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{  
  3.     //上拉加载更多  
  4.     public static final int  PULLUP_LOAD_MORE=0;  
  5.     //正在加载中  
  6.     public static final int  LOADING_MORE=1;  
  7.     //上拉加载更多状态-默认为0  
  8.     private int load_more_status=0;  
  9.     private LayoutInflater mInflater;  
  10.     private List<String> mTitles=null;  
  11.     private static final intTYPE_ITEM = 0;  //普通Item View  
  12.     private static final intTYPE_FOOTER = 1;  //顶部FootView  
  13.     public RefreshFootAdapter(Context context){  
  14.        this.mInflater=LayoutInflater.from(context);  
  15.         this.mTitles=new ArrayList<String>();  
  16.         for (int i=0;i<20;i++){  
  17.             int index=i+1;  
  18.            mTitles.add("item"+index);  
  19.         }  
  20.     }  
  21.     /** 
  22.      * item显示类型 
  23.      * @param parent 
  24.      * @param viewType 
  25.      * @return 
  26.      */  
  27.     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  28.         //进行判断显示类型,来创建返回不同的View  
  29.         if(viewType==TYPE_ITEM){  
  30.             Viewview=mInflater.inflate(R.layout.item_recycler_layout,parent,false);  
  31.             //这边可以做一些属性设置,甚至事件监听绑定  
  32.            //view.setBackgroundColor(Color.RED);  
  33.             ItemViewHolder itemViewHolder=new ItemViewHolder(view);  
  34.             return itemViewHolder;  
  35.         }else if(viewType==TYPE_FOOTER){  
  36.             Viewfoot_view=mInflater.inflate(R.layout.recycler_load_more_layout,parent,false);  
  37.             //这边可以做一些属性设置,甚至事件监听绑定  
  38.            //view.setBackgroundColor(Color.RED);  
  39.             FootViewHolder footViewHolder=new FootViewHolder(foot_view);  
  40.             return footViewHolder;  
  41.         }  
  42.        return null;  
  43.     }  
  44.    
  45.     /** 
  46.      * 数据的绑定显示 
  47.      * @param holder 
  48.      * @param position 
  49.      */  
  50.     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  51.         if(holder instanceof ItemViewHolder) {  
  52.            ((ItemViewHolder)holder).item_tv.setText(mTitles.get(position));  
  53.             holder.itemView.setTag(position);  
  54.         }else if(holder instanceof FootViewHolder){  
  55.             FootViewHolder footViewHolder=(FootViewHolder)holder;  
  56.             switch (load_more_status){  
  57.                 case PULLUP_LOAD_MORE:  
  58.                    footViewHolder.foot_view_item_tv.setText("上拉加载更多...");  
  59.                     break;  
  60.                 case LOADING_MORE:  
  61.                    footViewHolder.foot_view_item_tv.setText("正在加载更多数据...");  
  62.                     break;  
  63.             }  
  64.         }  
  65.     }  
  66.    
  67.     /** 
  68.      * 进行判断是普通Item视图还是FootView视图 
  69.      * @param position 
  70.      * @return 
  71.      */  
  72.     @Override  
  73.     public int getItemViewType(int position) {  
  74.     // 最后一个item设置为footerView  
  75.     if (position + 1 == getItemCount()) {  
  76.                 return TYPE_FOOTER;  
  77.             } else {  
  78.                 return TYPE_ITEM;  
  79.             }  
  80.         }  
  81.     @Override  
  82.     public int getItemCount() {  
  83.         return mTitles.size()+1;  
  84.     }  
  85.     //自定义的ViewHolder,持有每个Item的的所有界面元素  
  86.     public static class ItemViewHolder extends RecyclerView.ViewHolder {  
  87.         public TextView item_tv;  
  88.         public ItemViewHolder(View view){  
  89.             super(view);  
  90.             item_tv = (TextView)view.findViewById(R.id.item_tv);  
  91.         }  
  92.     }  
  93.     /** 
  94.      * 底部FootView布局 
  95.      */  
  96.     public static class FootViewHolder extends  RecyclerView.ViewHolder{  
  97.         private TextView foot_view_item_tv;  
  98.         public FootViewHolder(View view) {  
  99.             super(view);  
  100.            foot_view_item_tv=(TextView)view.findViewById(R.id.foot_view_item_tv);  
  101.         }  
  102.     }  
  103.    
  104.     //添加数据  
  105.     public void addItem(List<String> newDatas) {  
  106.         //mTitles.add(position, data);  
  107.         //notifyItemInserted(position);  
  108.         newDatas.addAll(mTitles);  
  109.         mTitles.removeAll(mTitles);  
  110.         mTitles.addAll(newDatas);  
  111.         notifyDataSetChanged();  
  112.     }  
  113.    
  114.     public void addMoreItem(List<String> newDatas) {  
  115.         mTitles.addAll(newDatas);  
  116.         notifyDataSetChanged();  
  117.     }  
  118.    
  119.     /** 
  120.      * //上拉加载更多 
  121.      * PULLUP_LOAD_MORE=0; 
  122.      * //正在加载中 
  123.      * LOADING_MORE=1; 
  124.      * //加载完成已经没有更多数据了 
  125.      * NO_MORE_DATA=2; 
  126.      * @param status 
  127.      */  
  128.     public void changeMoreStatus(int status){  
  129.         load_more_status=status;  
  130.         notifyDataSetChanged();  
  131.     }  
  132. }  

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

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. demo_recycler.setOnScrollListener(new RecyclerView.OnScrollListener() {  
  2.             @Override  
  3.             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  
  4.                super.onScrollStateChanged(recyclerView, newState);  
  5.                 if (newState ==RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 ==adapter.getItemCount()) {  
  6.                    adapter.changeMoreStatus(RefreshFootAdapter.LOADING_MORE);  
  7.                     newHandler().postDelayed(new Runnable() {  
  8.                         @Override  
  9.                         public void run() {  
  10.                             List<String> newDatas = new ArrayList<String>();  
  11.                             for (int i = 0; i< 5; i++) {  
  12.                                 int index = i +1;  
  13.                                newDatas.add("more item" + index);  
  14.                             }  
  15.                            adapter.addMoreItem(newDatas);  
  16.                            adapter.changeMoreStatus(RefreshFootAdapter.PULLUP_LOAD_MORE);  
  17.                         }  
  18.                     }, 2500);  
  19.                 }  
  20.             }  
  21.             @Override  
  22.             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {  
  23.                 super.onScrolled(recyclerView,dx, dy);  
  24.                 lastVisibleItem =linearLayoutManager.findLastVisibleItemPosition();  
  25.             }  
  26.         });  

查看代码之后,大家肯定也发现在onScrollStateChanged()方法中,Adapter.addMoreItem()和Adapter.changeMoreStatus()方法内部都调用了notifyDataSetChanged()方法,也就是说这边刷新了两次,针对这个问题,大家可以把第二个方法放入到addMoreItem()内部去,调用一次刷新操作接口。同时也可以自己按照实际需求进行优化。

8.运行效果如下:


(4).最后总结

           今天我们通过SwipeRefreshLayoutRecyclerView结合以及改造Adapter方式实现了RecyclerView的下拉刷新和上拉加载更多的效果。





演示Demo下载

转自:http://blog.csdn.net/u013443865/article/details/52694643

From Sky丶清(http://blog.csdn.net/developer_jiangqq

以上纯属于个人平时工作和学习的一些总结分享,如果有什么错误欢迎随时指出,大家可以讨论一起进步。

相关文章推荐

SwipeRefreshLayout详解和自定义上拉加载更多

SwipeRefreshLayout详解和自定义上拉加载更多 作者 PingerOne 关注 2016.09.26 19:02* 字数 2340 阅读 11773评论 35喜欢 107 ...

SwipeRefreshLayout详解和自定义加载更多

SwipeRefreshLayout详解和自定义加载更多个人主页 演示Demo下载 本文重点介绍了SwipeRefreshLayout的使用和自定View继承SwipeRefreshLayout...

SwipeRefreshLayout自定义上拉加载更多问题解决和完善

SwipeRefreshLayout自定义上拉加载更多问题解决和完善

使用SwipeRefreshLayout和自定义listview实现下拉刷新上啦加载更多

本文转载自http://www.2cto.com/kf/201505/399695.html 开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,之前大家最常用的应该是pul...

探索SwipeRefreshLayout配合自定义ListView完成下拉刷新、滑到底部自动加载更多

在Android开发过程中经常需要实现上下拉刷新功能,Google推出的下拉刷新控件SwipeRefreshLayout(彩虹条),由于官方版本只有下拉刷新而没有上拉加载更多的功能,很多人也尝试在这个...

SwipeRefreshLayout+RecyclerView 完成下拉刷新,上拉加载更多的自定义控件,简单好用

下拉刷新,上拉加载更多在许多应用中都频频用到,特别是需要分页加载数据时.之前在项目中经常用到的是网上非常普遍的PullToRefreshLayout.该框架比较好用,不过是比较旧了.就想着重新找一个控...
  • wzhseu
  • wzhseu
  • 2016年12月06日 14:18
  • 433
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android开发之SwipeRefreshLayout详解和自定义加载更多
举报原因:
原因补充:

(最多只允许输入30个字)