zoukankan      html  css  js  c++  java
  • Android5.0新特性:RecyclerView实现上拉加载更多

      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
     1     class NormalHolder extends RecyclerView.ViewHolder {
     2         ImageView img;
     3         TextView name;
     4 
     5         public NormalHolder(View itemView) {
     6             super(itemView);
     7             img = (ImageView) itemView.findViewById(R.id.homepage_grid_picpic);
     8             name = (TextView) itemView.findViewById(R.id.homepage_grid_name);
     9         }
    10 
    11         public void setData(int position) {
    12             imageLoader.displayImage(context, imgUrls[position % imgUrls.length], img);
    13             name.setText(dataList.get(position));
    14         }
    15     }

      

    FooterHolder
     1     public class FooterHolder extends RecyclerView.ViewHolder {
     2         View mLoadingViewstubstub;
     3         View mEndViewstub;
     4         View mNetworkErrorViewstub;
     5 
     6         public FooterHolder(View itemView) {
     7             super(itemView);
     8             mLoadingViewstubstub = itemView.findViewById(R.id.loading_viewstub);
     9             mEndViewstub = itemView.findViewById(R.id.end_viewstub);
    10             mNetworkErrorViewstub = itemView.findViewById(R.id.network_error_viewstub);
    11         }
    12 
    13         //根据传过来的status控制哪个状态可见
    14         public void setData(LoadingFooter.FooterState status) {
    15             Log.d("TAG", "reduAdapter" + status + "");
    16             switch (status) {
    17                 case Normal:
    18                     setAllGone();
    19                     break;
    20                 case Loading:
    21                     setAllGone();
    22                     mLoadingViewstubstub.setVisibility(View.VISIBLE);
    23                     break;
    24                 case TheEnd:
    25                     setAllGone();
    26                     mEndViewstub.setVisibility(View.VISIBLE);
    27                     break;
    28                 case NetWorkError:
    29                     setAllGone();
    30                     mNetworkErrorViewstub.setVisibility(View.VISIBLE);
    31                     break;
    32                 default:
    33                     break;
    34             }
    35 
    36         }
    37 
    38         //全部不可见
    39         void setAllGone() {
    40             if (mLoadingViewstubstub != null) {
    41                 mLoadingViewstubstub.setVisibility(View.GONE);
    42             }
    43             if (mEndViewstub != null) {
    44                 mEndViewstub.setVisibility(View.GONE);
    45             }
    46             if (mNetworkErrorViewstub != null) {
    47                 mNetworkErrorViewstub.setVisibility(View.GONE);
    48             }
    49         }
    50 
    51     }

      

      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完整代码:
      1 package com.yctime.truelove.fragment;
      2 
      3 
      4 import android.support.v4.app.Fragment;
      5 import android.support.v7.widget.DefaultItemAnimator;
      6 import android.support.v7.widget.GridLayoutManager;
      7 import android.support.v7.widget.LinearLayoutManager;
      8 import android.support.v7.widget.RecyclerView;
      9 import android.util.Log;
     10 import android.view.LayoutInflater;
     11 import android.view.View;
     12 
     13 import com.yctime.truelove.LoadMore.EndlessRecyclerOnScrollListener;
     14 import com.yctime.truelove.LoadMore.LoadingFooter;
     15 import com.yctime.truelove.MainActivity;
     16 import com.yctime.truelove.Utils.NetworkUtils;
     17 import com.yctime.truelove.login.R;
     18 
     19 import java.util.ArrayList;
     20 
     21 
     22 /**
     23  * A simple {@link Fragment} subclass.
     24  */
     25 public class HomeReDuFragment extends BaseFragment {
     26 
     27     private RecyclerView mRecyclerView;
     28     private GridAdapter_Redu gridReDuAdapter;
     29     // 服务器端一共多少条数据
     30     private static final int TOTAL_COUNTER = 50;
     31     // 每一页展示多少条数据
     32     private static final int REQUEST_COUNT = 12;
     33     // 已经获取到多少条数据了
     34     private int mCurrentCounter = 0;
     35     //模拟的数据源
     36     private ArrayList<String> dataList;
     37 
     38 
     39     protected LoadingFooter.FooterState mState = LoadingFooter.FooterState.Normal;
     40 
     41     protected void setState(LoadingFooter.FooterState mState) {
     42         this.mState = mState;
     43         ((MainActivity) mContext).runOnUiThread(new Runnable() {
     44             @Override
     45             public void run() {
     46                 changeAdaperState();
     47             }
     48         });
     49     }
     50 
     51     //改变底部bottom的样式
     52     protected void changeAdaperState() {
     53         if (gridReDuAdapter != null && gridReDuAdapter.mFooterHolder != null) {
     54             gridReDuAdapter.mFooterHolder.setData(mState);
     55         }
     56     }
     57 
     58     public HomeReDuFragment() {
     59     }
     60 
     61 
     62     @Override
     63     protected View initView() {
     64         View mView = LayoutInflater.from(mContext).inflate(R.layout.homepage_viewpager_item_redu, null);
     65         mRecyclerView = (RecyclerView) mView.findViewById(R.id.home_page_recyclerview);
     66         return mView;
     67     }
     68 
     69     @Override
     70     protected void initData() {
     71         initGridView();
     72     }
     73 
     74 
     75     private View initGridView() {
     76         mRecyclerView.setHasFixedSize(true);
     77         //滑动暂停加载网络图片,而且可以监听recycler是否滑动到底部
     78         mRecyclerView.addOnScrollListener(mOnScrollListener);
     79         mRecyclerView.setItemAnimator(new DefaultItemAnimator());
     80         gridReDuAdapter = new GridAdapter_Redu(mContext);
     81         gridReDuAdapter.addAll(getRemoteData());
     82         mRecyclerView.setAdapter(gridReDuAdapter);
     83         GridLayoutManager layoutManager = new GridLayoutManager(mContext, 3);
     84         layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
     85         layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
     86             @Override
     87             public int getSpanSize(int position) {
     88                 //如果是最后一个item,则设置占据3列,否则占据1列
     89                 boolean isFooter = position == gridReDuAdapter.getItemCount() - 1;
     90                 return isFooter ? 3 : 1;
     91             }
     92         });
     93         mRecyclerView.setLayoutManager(layoutManager);
     94         return mRecyclerView;
     95     }
     96 
     97     private EndlessRecyclerOnScrollListener mOnScrollListener = new EndlessRecyclerOnScrollListener() {
     98         @Override
     99         public void onLoadNextPage(View view) {
    100             super.onLoadNextPage(view);
    101 
    102             if (mState == LoadingFooter.FooterState.Loading) {
    103                 Log.d("@TAG", "the state is Loading, just wait..");
    104                 return;
    105             }
    106 
    107             if (mCurrentCounter < TOTAL_COUNTER) {
    108                 // loading more
    109                 requestData();
    110                 Log.d("TAG", "请求数据");
    111             } else {
    112                 //the end
    113                 setState(LoadingFooter.FooterState.TheEnd);
    114             }
    115         }
    116     };
    117 
    118 
    119     /**
    120      * 模拟请求网络
    121      */
    122     private void requestData() {
    123         setState(LoadingFooter.FooterState.Loading);
    124         new Thread() {
    125             @Override
    126             public void run() {
    127                 super.run();
    128                 try {
    129                     Thread.sleep(1000);
    130                 } catch (InterruptedException e) {
    131                     e.printStackTrace();
    132                 }
    133                 if (NetworkUtils.isNetAvailable(mContext)) {
    134                     //模拟请求远程数据
    135                     gridReDuAdapter.addAll(getRemoteData());
    136                     //加载完毕时
    137                     setState(LoadingFooter.FooterState.Normal);
    138                     Log.d("TAG", mCurrentCounter + "");
    139                 } else {
    140                     //模拟一下网络请求失败的情况
    141                     setState(LoadingFooter.FooterState.NetWorkError);
    142                 }
    143             }
    144         }.start();
    145     }
    146 
    147     //模拟请求数据
    148     private ArrayList<String> getRemoteData() {
    149         if (dataList == null)
    150             dataList = new ArrayList<>();
    151         //每次都清空一下
    152         dataList.clear();
    153         //要减去adapter最后一页
    154         for (int i = 0; i < REQUEST_COUNT; i++) {
    155             if (dataList.size() + mCurrentCounter >= TOTAL_COUNTER) {
    156                 break;
    157             }
    158             dataList.add("账号" + (mCurrentCounter + i));
    159         }
    160         mCurrentCounter += dataList.size();
    161         return dataList;
    162     }
    163 
    164 
    165 }
    需要注意的是这个方法:
      //改变底部bottom的样式
        protected void changeAdaperState() {
            if (gridReDuAdapter != null && gridReDuAdapter.mFooterHolder != null) {
                gridReDuAdapter.mFooterHolder.setData(mState);
            }
        }
    Adapter的应用调用Adapter里面的方法,来切换Adaper的样式。
      Adapter完整代码:
      1 package com.yctime.truelove.fragment;
      2 
      3 import android.content.Context;
      4 import android.content.Intent;
      5 import android.support.v7.widget.RecyclerView;
      6 import android.util.Log;
      7 import android.view.LayoutInflater;
      8 import android.view.View;
      9 import android.view.ViewGroup;
     10 import android.widget.ImageView;
     11 import android.widget.TextView;
     12 
     13 import com.yctime.truelove.ImageLoader.MyImageLoader;
     14 import com.yctime.truelove.ImageLoader.UILImageLoader;
     15 import com.yctime.truelove.LoadMore.LoadingFooter;
     16 import com.yctime.truelove.drawer.MyZoneActivity;
     17 import com.yctime.truelove.login.R;
     18 
     19 import java.util.ArrayList;
     20 
     21 
     22 public class GridAdapter_Redu extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
     23 
     24 
     25     public String[] imgUrls = {
     26             "http://img5.duitang.com/uploads/item/201402/22/20140222113830_X3ddd.jpeg",
     27             "http://v1.qzone.cc/avatar/201403/01/10/36/531147afa4197738.jpg!200x200.jpg",
     28             "http://g.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=e4d7ed147af40ad115b1cfe7621c3de9/b7fd5266d016092445b47837d50735fae6cd340d.jpg",
     29             "http://img5q.duitang.com/uploads/item/201502/19/20150219182507_vGVaK.jpeg",
     30             "http://p1.qqyou.com/touxiang/uploadpic/2013-3/12/2013031212190118646.jpg",
     31             "http://img5.duitang.com/uploads/item/201412/08/20141208221323_YVJFk.png",
     32             "http://cdn.duitang.com/uploads/item/201408/02/20140802222651_GWuU2.png",
     33             "http://ent.dzwww.com/yulezhuanti/mtcbg/201510/W020151027467479100669.jpg",
     34             "http://p1.qqyou.com/touxiang/uploadpic/2013-3/10/2013031009323656495.jpg",
     35             "http://p1.qqyou.com/touxiang/uploadpic/2013-3/12/2013031212295986807.jpg",
     36             "http://f.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=10742594d739b6004d9b07b1d9601912/9f2f070828381f30ec9eabdeab014c086f06f0c5.jpg",
     37             "http://a.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=5bda8a18a71ea8d38a777c02a73a1c76/5882b2b7d0a20cf4598dc37c77094b36acaf9977.jpg",
     38             "http://a1.att.hudong.com/36/98/300001051406133039983418031.jpg"
     39     };
     40     public Context context;
     41     MyImageLoader imageLoader = new UILImageLoader();
     42     private ArrayList<String> dataList = new ArrayList<>();
     43 
     44 
     45     private final int NORMALLAYOUT = 0;
     46     private final int FOOTERLAYOUT = 1;
     47     public FooterHolder mFooterHolder;
     48 
     49     public GridAdapter_Redu(Context context) {
     50         this.context = context;
     51     }
     52 
     53     public void addAll(ArrayList<String> list) {
     54         int lastIndex = this.dataList.size();
     55         if (this.dataList.addAll(list)) {
     56             notifyItemRangeInserted(lastIndex, list.size());
     57         }
     58     }
     59 
     60     @Override
     61     public int getItemViewType(int position) {
     62         if (position == dataList.size())
     63             return FOOTERLAYOUT;
     64         else
     65             return NORMALLAYOUT;
     66     }
     67 
     68 
     69     @Override
     70     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     71         View view;
     72         if (viewType == NORMALLAYOUT) {
     73             view = LayoutInflater.from(context).inflate(R.layout.grid_redu_item, parent, false);
     74             return new NormalHolder(view);
     75         } else {
     76             view = LayoutInflater.from(context).inflate(R.layout.sample_common_list_footer, parent, false);
     77             mFooterHolder = new FooterHolder(view);
     78             return mFooterHolder;
     79         }
     80     }
     81 
     82     @Override
     83     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
     84         if (holder instanceof NormalHolder) {
     85             //点击事件
     86             holder.itemView.setOnClickListener(new View.OnClickListener() {
     87                 @Override
     88                 public void onClick(View v) {
     89                     Intent intent = new Intent(context, MyZoneActivity.class);
     90                     context.startActivity(intent);
     91                 }
     92             });
     93             ((NormalHolder) holder).setData(position);
     94         }
     95     }
     96 
     97     @Override
     98     public int getItemCount() {
     99         return dataList.size() + 1;
    100     }
    101 
    102 
    103     class NormalHolder extends RecyclerView.ViewHolder {
    104         ImageView img;
    105         TextView name;
    106 
    107         public NormalHolder(View itemView) {
    108             super(itemView);
    109             img = (ImageView) itemView.findViewById(R.id.homepage_grid_picpic);
    110             name = (TextView) itemView.findViewById(R.id.homepage_grid_name);
    111         }
    112 
    113         public void setData(int position) {
    114             imageLoader.displayImage(context, imgUrls[position % imgUrls.length], img);
    115             name.setText(dataList.get(position));
    116         }
    117     }
    118 
    119     public class FooterHolder extends RecyclerView.ViewHolder {
    120         View mLoadingViewstubstub;
    121         View mEndViewstub;
    122         View mNetworkErrorViewstub;
    123 
    124         public FooterHolder(View itemView) {
    125             super(itemView);
    126             mLoadingViewstubstub = itemView.findViewById(R.id.loading_viewstub);
    127             mEndViewstub = itemView.findViewById(R.id.end_viewstub);
    128             mNetworkErrorViewstub = itemView.findViewById(R.id.network_error_viewstub);
    129         }
    130 
    131         //根据传过来的status控制哪个状态可见
    132         public void setData(LoadingFooter.FooterState status) {
    133             Log.d("TAG", "reduAdapter" + status + "");
    134             switch (status) {
    135                 case Normal:
    136                     setAllGone();
    137                     break;
    138                 case Loading:
    139                     setAllGone();
    140                     mLoadingViewstubstub.setVisibility(View.VISIBLE);
    141                     break;
    142                 case TheEnd:
    143                     setAllGone();
    144                     mEndViewstub.setVisibility(View.VISIBLE);
    145                     break;
    146                 case NetWorkError:
    147                     setAllGone();
    148                     mNetworkErrorViewstub.setVisibility(View.VISIBLE);
    149                     break;
    150                 default:
    151                     break;
    152             }
    153 
    154         }
    155 
    156         //全部不可见
    157         void setAllGone() {
    158             if (mLoadingViewstubstub != null) {
    159                 mLoadingViewstubstub.setVisibility(View.GONE);
    160             }
    161             if (mEndViewstub != null) {
    162                 mEndViewstub.setVisibility(View.GONE);
    163             }
    164             if (mNetworkErrorViewstub != null) {
    165                 mNetworkErrorViewstub.setVisibility(View.GONE);
    166             }
    167         }
    168 
    169     }
    170 
    171 }
    运行效果:

     
    
    
    
    
    
    
     

       

    
    
    
    
    
    

      

     
     
     

      

  • 相关阅读:
    Ubuntu 18.04 安装博通(Broadcom)无线网卡驱动
    Python3漏洞扫描工具 ( Python3 插件式框架 )
    Linux 防火墙
    基于Python3的漏洞检测工具 ( Python3 插件式框架 )
    git学习笔记
    sublime text 3 优化配置
    win10 出现0x80072efd错误
    Ubuntu搭建NFS服务器,NFS协议详细分析
    docker实现跨主机连接
    Python-RabbitMQ(持久化)
  • 原文地址:https://www.cnblogs.com/xjx22/p/5397735.html
Copyright © 2011-2022 走看看