• RecyclerView--添加头部和底部


    1.先构建WrapRecyclerAdapter

      1 /**
      2  * Description: 可以添加头部和底部的Adapter
      3  */
      4 public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
      5     private final static String TAG = "WrapRecyclerAdapter";
      6 
      7     /**
      8      * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
      9      * there can be gaps in the indices.  It is intended to be more memory efficient
     10      * than using a HashMap to map Integers to Objects, both because it avoids
     11      * auto-boxing keys and its data structure doesn't rely on an extra entry object
     12      * for each mapping.
     13      *
     14      * SparseArray是一个<int , Object>的HashMap  比HashMap更高效
     15      */
     16     private SparseArray<View> mHeaderViews;
     17     private SparseArray<View> mFooterViews;
     18 
     19     // 基本的头部类型开始位置  用于viewType
     20     private static int BASE_ITEM_TYPE_HEADER = 10000000;
     21     // 基本的底部类型开始位置  用于viewType
     22     private static int BASE_ITEM_TYPE_FOOTER = 20000000;
     23 
     24     /**
     25      * 数据列表的Adapter
     26      */
     27     private RecyclerView.Adapter mAdapter;
     28 
     29     public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
     30         this.mAdapter = adapter;
     31         mHeaderViews = new SparseArray<>();
     32         mFooterViews = new SparseArray<>();
     33     }
     34 
     35     @Override
     36     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     37 
     38         // viewType 可能就是 SparseArray 的key
     39         if (isHeaderViewType(viewType)) {
     40             View headerView = mHeaderViews.get(viewType);
     41             return createHeaderFooterViewHolder(headerView);
     42         }
     43 
     44         if (isFooterViewType(viewType)) {
     45             View footerView = mFooterViews.get(viewType);
     46             return createHeaderFooterViewHolder(footerView);
     47         }
     48         return mAdapter.onCreateViewHolder(parent, viewType);
     49     }
     50 
     51     /**
     52      * 是不是底部类型
     53      */
     54     private boolean isFooterViewType(int viewType) {
     55         int position = mFooterViews.indexOfKey(viewType);
     56         return position >= 0;
     57     }
     58 
     59     /**
     60      * 创建头部或者底部的ViewHolder
     61      */
     62     private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
     63         return new RecyclerView.ViewHolder(view) {
     64 
     65         };
     66     }
     67 
     68     /**
     69      * 是不是头部类型
     70      */
     71     private boolean isHeaderViewType(int viewType) {
     72         int position = mHeaderViews.indexOfKey(viewType);
     73         return position >= 0;
     74     }
     75 
     76     @Override
     77     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
     78         if (isHeaderPosition(position) || isFooterPosition(position)) {
     79             return;
     80         }
     81 
     82         // 计算一下位置
     83         final int adapterPosition = position - mHeaderViews.size();
     84         mAdapter.onBindViewHolder(holder, adapterPosition);
     85 
     86         // 设置点击和长按事件
     87         if (mItemClickListener != null) {
     88             holder.itemView.setOnClickListener(new View.OnClickListener() {
     89                 @Override
     90                 public void onClick(View v) {
     91                     mItemClickListener.onItemClick(v, adapterPosition);
     92                 }
     93             });
     94         }
     95         if (mLongClickListener != null) {
     96             holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
     97                 @Override
     98                 public boolean onLongClick(View v) {
     99                     return mLongClickListener.onLongClick(v, adapterPosition);
    100                 }
    101             });
    102         }
    103     }
    104 
    105     @Override
    106     public int getItemViewType(int position) {
    107         if (isHeaderPosition(position)) {
    108             // 直接返回position位置的key
    109             return mHeaderViews.keyAt(position);
    110         }
    111         if (isFooterPosition(position)) {
    112             // 直接返回position位置的key
    113             position = position - mHeaderViews.size() - mAdapter.getItemCount();
    114             return mFooterViews.keyAt(position);
    115         }
    116         // 返回列表Adapter的getItemViewType
    117         position = position - mHeaderViews.size();
    118         return mAdapter.getItemViewType(position);
    119     }
    120 
    121     /**
    122      * 是不是底部位置
    123      */
    124     private boolean isFooterPosition(int position) {
    125         return position >= (mHeaderViews.size() + mAdapter.getItemCount());
    126     }
    127 
    128     /**
    129      * 是不是头部位置
    130      */
    131     private boolean isHeaderPosition(int position) {
    132         return position < mHeaderViews.size();
    133     }
    134 
    135     @Override
    136     public int getItemCount() {
    137         // 条数三者相加 = 底部条数 + 头部条数 + Adapter的条数
    138         return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
    139     }
    140 
    141     /**
    142      * 获取列表的Adapter
    143      */
    144     private RecyclerView.Adapter getAdapter() {
    145         return mAdapter;
    146     }
    147 
    148     // 添加头部
    149     public void addHeaderView(View view) {
    150         int position = mHeaderViews.indexOfValue(view);
    151         if (position < 0) {
    152             mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
    153         }
    154         notifyDataSetChanged();
    155     }
    156 
    157     // 添加底部
    158     public void addFooterView(View view) {
    159         int position = mFooterViews.indexOfValue(view);
    160         if (position < 0) {
    161             mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);
    162         }
    163         notifyDataSetChanged();
    164     }
    165 
    166     // 移除头部
    167     public void removeHeaderView(View view) {
    168         int index = mHeaderViews.indexOfValue(view);
    169         if (index < 0) return;
    170         mHeaderViews.removeAt(index);
    171         notifyDataSetChanged();
    172     }
    173 
    174     // 移除底部
    175     public void removeFooterView(View view) {
    176         int index = mFooterViews.indexOfValue(view);
    177         if (index < 0) return;
    178         mFooterViews.removeAt(index);
    179         notifyDataSetChanged();
    180     }
    181 
    182     /**
    183      * 解决GridLayoutManager添加头部和底部不占用一行的问题
    184      *
    185      * @param recycler
    186      * @version 1.0
    187      */
    188     public void adjustSpanSize(RecyclerView recycler) {
    189         if (recycler.getLayoutManager() instanceof GridLayoutManager) {
    190             final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
    191             layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    192                 @Override
    193                 public int getSpanSize(int position) {
    194                     boolean isHeaderOrFooter =
    195                             isHeaderPosition(position) || isFooterPosition(position);
    196                     return isHeaderOrFooter ? layoutManager.getSpanCount() : 1;
    197                 }
    198             });
    199         }
    200     }
    201 
    202     /***************
    203      * 给条目设置点击和长按事件
    204      *********************/
    205     public OnItemClickListener mItemClickListener;
    206     public OnLongClickListener mLongClickListener;
    207 
    208     public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    209         this.mItemClickListener = itemClickListener;
    210     }
    211 
    212     public void setOnLongClickListener(OnLongClickListener longClickListener) {
    213         this.mLongClickListener = longClickListener;
    214     }
    215 }

    2.构建WrapRecyclerView

    我们最好还是模仿ListView的结构搞就搞到西,自定义一个WrapRecyclerView,可以添加删除头部和底部View,这个就比较简单

      1 /**
      2  * Description: 可以添加头部和底部的RecyclerView
      3  */
      4 public class WrapRecyclerView extends RecyclerView {
      5     // 包裹了一层的头部底部Adapter
      6     private WrapRecyclerAdapter mWrapRecyclerAdapter;
      7     // 这个是列表数据的Adapter
      8     private Adapter mAdapter;
      9 
     10     // 增加一些通用功能
     11     // 空列表数据应该显示的空View
     12     // 正在加载数据页面,也就是正在获取后台接口页面
     13     private View mEmptyView, mLoadingView;
     14 
     15     public WrapRecyclerView(Context context) {
     16         super(context);
     17     }
     18 
     19     public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
     20         super(context, attrs);
     21     }
     22 
     23     public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
     24         super(context, attrs, defStyle);
     25     }
     26 
     27     @Override
     28     public void setAdapter(Adapter adapter) {
     29         // 为了防止多次设置Adapter
     30         if (mAdapter != null) {
     31             mAdapter.unregisterAdapterDataObserver(mDataObserver);
     32             mAdapter = null;
     33         }
     34 
     35         this.mAdapter = adapter;
     36 
     37         if (adapter instanceof WrapRecyclerAdapter) {
     38             mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
     39         } else {
     40             mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
     41         }
     42 
     43         super.setAdapter(mWrapRecyclerAdapter);
     44 
     45         // 注册一个观察者
     46         mAdapter.registerAdapterDataObserver(mDataObserver);
     47 
     48         // 解决GridLayout添加头部和底部也要占据一行
     49         mWrapRecyclerAdapter.adjustSpanSize(this);
     50 
     51         // 加载数据页面
     52         if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) {
     53             mLoadingView.setVisibility(View.GONE);
     54         }
     55 
     56         if (mItemClickListener != null) {
     57             mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
     58         }
     59 
     60         if (mLongClickListener != null) {
     61             mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
     62         }
     63     }
     64 
     65     // 添加头部
     66     public void addHeaderView(View view) {
     67         // 如果没有Adapter那么就不添加,也可以选择抛异常提示
     68         // 让他必须先设置Adapter然后才能添加,这里是仿照ListView的处理方式
     69         if (mWrapRecyclerAdapter != null) {
     70             mWrapRecyclerAdapter.addHeaderView(view);
     71         }
     72     }
     73 
     74     // 添加底部
     75     public void addFooterView(View view) {
     76         if (mWrapRecyclerAdapter != null) {
     77             mWrapRecyclerAdapter.addFooterView(view);
     78         }
     79     }
     80 
     81     // 移除头部
     82     public void removeHeaderView(View view) {
     83         if (mWrapRecyclerAdapter != null) {
     84             mWrapRecyclerAdapter.removeHeaderView(view);
     85         }
     86     }
     87 
     88     // 移除底部
     89     public void removeFooterView(View view) {
     90         if (mWrapRecyclerAdapter != null) {
     91             mWrapRecyclerAdapter.removeFooterView(view);
     92         }
     93     }
     94 
     95     private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
     96         @Override
     97         public void onChanged() {
     98             if (mAdapter == null) return;
     99             // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
    100             if (mWrapRecyclerAdapter != mAdapter)
    101                 mWrapRecyclerAdapter.notifyDataSetChanged();
    102 
    103             dataChanged();
    104         }
    105 
    106         @Override
    107         public void onItemRangeRemoved(int positionStart, int itemCount) {
    108             if (mAdapter == null) return;
    109             // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
    110             if (mWrapRecyclerAdapter != mAdapter)
    111                 mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
    112             dataChanged();
    113         }
    114 
    115         @Override
    116         public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
    117             if (mAdapter == null) return;
    118             // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved没效果
    119             if (mWrapRecyclerAdapter != mAdapter)
    120                 mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
    121             dataChanged();
    122         }
    123 
    124         @Override
    125         public void onItemRangeChanged(int positionStart, int itemCount) {
    126             if (mAdapter == null) return;
    127             // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
    128             if (mWrapRecyclerAdapter != mAdapter)
    129                 mWrapRecyclerAdapter.notifyItemChanged(positionStart);
    130             dataChanged();
    131         }
    132 
    133         @Override
    134         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
    135             if (mAdapter == null) return;
    136             // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
    137             if (mWrapRecyclerAdapter != mAdapter)
    138                 mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
    139             dataChanged();
    140         }
    141 
    142         @Override
    143         public void onItemRangeInserted(int positionStart, int itemCount) {
    144             if (mAdapter == null) return;
    145             // 观察者  列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted没效果
    146             if (mWrapRecyclerAdapter != mAdapter)
    147                 mWrapRecyclerAdapter.notifyItemInserted(positionStart);
    148             dataChanged();
    149         }
    150     };
    151 
    152     /**
    153      * 添加一个空列表数据页面
    154      */
    155     public void addEmptyView(View emptyView) {
    156         this.mEmptyView = emptyView;
    157     }
    158 
    159     /**
    160      * 添加一个正在加载数据的页面
    161      */
    162     public void addLoadingView(View loadingView) {
    163         this.mLoadingView = loadingView;
    164         mLoadingView.setVisibility(View.VISIBLE);
    165     }
    166 
    167     /**
    168      * Adapter数据改变的方法
    169      */
    170     private void dataChanged() {
    171         if (mAdapter.getItemCount() == 0) {
    172             // 没有数据
    173             if (mEmptyView != null) {
    174                 mEmptyView.setVisibility(VISIBLE);
    175             }
    176         } else {
    177             // 没有数据
    178             if (mEmptyView != null) {
    179                 mEmptyView.setVisibility(GONE);
    180             }
    181         }
    182     }
    183 
    184     /***************
    185      * 给条目设置点击和长按事件
    186      *********************/
    187     public com.zzw.framelibray.recyclerview.adapter.OnItemClickListener mItemClickListener;
    188     public com.zzw.framelibray.recyclerview.adapter.OnLongClickListener mLongClickListener;
    189 
    190     public void setOnItemClickListener(com.zzw.framelibray.recyclerview.adapter.OnItemClickListener itemClickListener) {
    191         this.mItemClickListener = itemClickListener;
    192 
    193         if (mWrapRecyclerAdapter != null) {
    194             mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
    195         }
    196     }
    197 
    198     public void setOnLongClickListener(com.zzw.framelibray.recyclerview.adapter.OnLongClickListener longClickListener) {
    199         this.mLongClickListener = longClickListener;
    200 
    201         if (mWrapRecyclerAdapter != null) {
    202             mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
    203         }
    204     }
    205 }

    3.使用:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="vertical" android:layout_width="match_parent"
     4     android:layout_height="match_parent">
     5 
     6     <com.zzw.framelibray.recyclerview.view.WrapRecyclerView
     7         android:id="@+id/wrap_recycler_view"
     8         android:layout_width="match_parent"
     9         android:layout_height="match_parent"
    10 
    11         />
    12 
    13 </LinearLayout>
    Activity
     1 public class HeaderFooterActivity extends AppCompatActivity implements OnItemClickListener {
     2 
     3     private WrapRecyclerView mRecyclerView;
     4     private List<People> mData;
     5 
     6     @Override
     7     protected void onCreate(Bundle savedInstanceState) {
     8         super.onCreate(savedInstanceState);
     9         setContentView(R.layout.activity_recycler_view);
    10         mRecyclerView = (WrapRecyclerView) findViewById(R.id.wrap_recycler_view);
    11         mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    12 
    13         mData =new ArrayList<>();
    14         mData.add(new People());
    15         mData.add(new People());
    16         mData.add(new People());
    17 
    18         PeopleListAdapter listAdapter = new PeopleListAdapter(this, mData);
    19 
    20         // 添加头部和底部 需要 包裹Adapter,才能添加头部和底部
    21         WrapRecyclerAdapter wrapRecyclerAdapter = new WrapRecyclerAdapter(listAdapter);
    22         mRecyclerView.setAdapter(wrapRecyclerAdapter);
    23 
    24         wrapRecyclerAdapter.setOnItemClickListener(this);
    25 
    26         // 添加头部和底部
    27         wrapRecyclerAdapter.addHeaderView(LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false));
    28         wrapRecyclerAdapter.addFooterView(LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false));
    29     }
    30 
    31     @Override
    32     public void onItemClick(View view, int position) {
    33         Toast.makeText(this, "" + mData.get(position).name, Toast.LENGTH_SHORT).show();
    34     }
    35 
    36     class PeopleListAdapter extends CommonRecyclerAdapter<People> {
    37 
    38         public PeopleListAdapter(Context context, List<People> datas) {
    39             super(context, datas, R.layout.channel_list_item);
    40         }
    41 
    42         @Override
    43         public void convert(ViewHolder holder, People item, int position) {
    44             holder.setText(R.id.action_btn, item.name+position);
    45         }
    46     }
    47 
    48     class People{
    49         String name="王伟:";
    50     }
    51 }
  • 相关阅读:
    糍粑大叔的独游之旅-战斗!之弹道实现
    攻击判定流程研究: 瀑布算法、圆桌算法、混合算法解析
    GitHub排名TOP30的机器学习开源项目/贪心学院
    学习ES7+ES8
    k8s Ipvs 内部网络自动分配和内部网络一致ip地址,导致ip冲突
    Linux操作系统load average过高,kworker占用较多cpu
    chrome断点调试&&其他技巧
    Mongodb更新数组$pull修饰符 (mongodb 修改器($inc/$set/$unset/$push/$pop/upsert))
    记一次线上Java程序导致服务器CPU占用率过高的问题排除过程
    解决并发问题,数据库常用的两把锁(转)
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/8275368.html
Copyright © 2020-2023  润新知