您现在的位置是:首页 > 文章详情

Android 解析RecyclerView(3)——以更简单的方法实现带顶部View和底部View的RecyclerView

日期:2017-06-06点击:263

在上一篇文章:解析RecyclerView(2)——带顶部View和底部View的RecyclerView ,我介绍了如何为RecyclerView 添加头部View和底部View,功能虽然实现了,不过需要用到的类也相对较多。其实可以像我在第一篇文章里介绍的使用泛型来适应不同情况那样,同样可以在 WrapRecyclerViewAdapter 中使用泛型,这样会使代码更为简洁

如果看过我前两篇文章了,以下代码应该就很容易理解了

一、通用ViewHolder

/** * 通用ViewHolder * Created by ZY on 2017/6/3. */ public class GeneralWrapRecyclerHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { public interface onClickCommonListener { void onClick(int position); void onLongClick(int position); } private onClickCommonListener clickCommonListener; //用来存放View以减少findViewById的次数 private SparseArray<View> viewSparseArray; public GeneralWrapRecyclerHolder(View itemView) { super(itemView); viewSparseArray = new SparseArray<>(); itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); } public void setClickCommonListener(onClickCommonListener clickCommonListener) { this.clickCommonListener = clickCommonListener; } @Override public void onClick(View view) { if (clickCommonListener != null) { clickCommonListener.onClick(getAdapterPosition()); } } @Override public boolean onLongClick(View view) { if (clickCommonListener != null) { clickCommonListener.onLongClick(getAdapterPosition()); } return true; } /** * 根据 ID 来获取 View * * @param viewId viewID * @param <T> 泛型 * @return 将结果强转为 View 或 View 的子类型 */ private <T extends View> T getView(int viewId) { // 先从缓存中找,找到的话则直接返回 // 如果找不到则findViewById,再把结果存入缓存中 View view = viewSparseArray.get(viewId); if (view == null) { view = itemView.findViewById(viewId); viewSparseArray.put(viewId, view); } return (T) view; } public GeneralWrapRecyclerHolder setText(int viewId, CharSequence text) { TextView tv = getView(viewId); tv.setText(text); return this; } public GeneralWrapRecyclerHolder setImageResource(int viewId, int resourceId) { ImageView imageView = getView(viewId); imageView.setImageResource(resourceId); return this; } public GeneralWrapRecyclerHolder setImageResource(int viewId, Bitmap bitmap) { ImageView imageView = getView(viewId); imageView.setImageBitmap(bitmap); return this; } public GeneralWrapRecyclerHolder setViewVisibility(int viewId, int visibility) { getView(viewId).setVisibility(visibility); return this; } } 

二、GeneralWrapRecyclerAdapter

/** * 可以带头部View与尾部View的RecyclerView Adapter * Created by CZY on 2017/6/4. */ public abstract class GeneralWrapRecyclerAdapter<T> extends RecyclerView.Adapter<GeneralWrapRecyclerHolder> { public interface MultiTypeSupport<T> { int getLayoutId(T item, int position); } private LayoutInflater layoutInflater; private List<T> dataList; private int layoutId; private MultiTypeSupport<T> multiTypeSupport; private GeneralWrapRecyclerHolder.onClickCommonListener clickCommonListener; private SparseArray<View> headerViews; private SparseArray<View> footerViews; //头部类型开始位置,用于viewType private int BASE_ITEM_TYPE_HEADER = 1000; //底部类型开始位置,用于viewType private int BASE_ITEM_TYPE_FOOTER = 2000; /** * 私有构造函数 * * @param context 上下文 * @param dataList 数据集合 */ private GeneralWrapRecyclerAdapter(Context context, List<T> dataList) { this.layoutInflater = LayoutInflater.from(context); this.dataList = dataList; headerViews = new SparseArray<>(); footerViews = new SparseArray<>(); } /** * 适用于:列表所有的子项都使用相同的布局文件,且不需要监听点击事件 * * @param context 上下文 * @param dataList 数据集合 * @param layoutId 布局文件ID */ protected GeneralWrapRecyclerAdapter(Context context, List<T> dataList, int layoutId) { this(context, dataList); this.layoutId = layoutId; } /** * 适用于:列表的子项使用不同的布局文件,且不需要监听点击事件 * * @param context 上下文 * @param dataList 数据集合 * @param multiTypeSupport 支持多个布局文件 */ protected GeneralWrapRecyclerAdapter(Context context, List<T> dataList, MultiTypeSupport<T> multiTypeSupport) { this(context, dataList); this.multiTypeSupport = multiTypeSupport; } /** * 适用于:列表所有的子项都使用相同的布局文件,且需要监听点击事件 * * @param context 上下文 * @param dataList 数据集合 * @param layoutId 布局文件ID * @param clickCommonListener 点击事件监听 */ protected GeneralWrapRecyclerAdapter(Context context, List<T> dataList, int layoutId, GeneralWrapRecyclerHolder.onClickCommonListener clickCommonListener) { this(context, dataList, layoutId); this.clickCommonListener = clickCommonListener; } /** * 适用于:列表的子项使用不同的布局文件,且需要监听点击事件 * * @param context 上下文 * @param dataList 数据集合 * @param multiTypeSupport 支持多个布局文件 * @param clickCommonListener 点击事件监听 */ protected GeneralWrapRecyclerAdapter(Context context, List<T> dataList, MultiTypeSupport<T> multiTypeSupport, GeneralWrapRecyclerHolder.onClickCommonListener clickCommonListener) { this(context, dataList, multiTypeSupport); this.clickCommonListener = clickCommonListener; } /** * 根据索引判断该位置的View类型 * 如果是头部,则返回该View在headerViews中的key * 如果是底部,则返回该View在footerViews中的key * * @param position 索引 * @return View类型 */ @Override public int getItemViewType(int position) { if (isHeaderPosition(position)) { return headerViews.keyAt(position); } if (isFooterPosition(position)) { position = position - headerViews.size() - getDataItemCount(); return footerViews.keyAt(position); } position = position - headerViews.size(); if (multiTypeSupport != null) { return multiTypeSupport.getLayoutId(dataList.get(position), position); } return super.getItemViewType(position); } @Override public GeneralWrapRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (isHeaderViewType(viewType)) { return createHeaderFooterViewHolder(headerViews.get(viewType)); } if (isFooterViewType(viewType)) { return createHeaderFooterViewHolder(footerViews.get(viewType)); } if (multiTypeSupport != null) { layoutId = viewType; } View view = layoutInflater.inflate(layoutId, parent, false); return new GeneralWrapRecyclerHolder(view); } @Override public void onBindViewHolder(GeneralWrapRecyclerHolder holder, int position) { holder.setClickCommonListener(clickCommonListener); if (isHeaderPosition(position) || isFooterPosition(position)) { return; } bindData(holder, dataList.get(position - getHeaderItemCount())); } /** * 获取列表总的条数(头部View个数+列表条数+底部View个数) * * @return 总的条数 */ @Override public int getItemCount() { return dataList.size() + headerViews.size() + footerViews.size(); } /** * 获取不包含头部和底部View之后列表的条数 * * @return 列表条数 */ private int getDataItemCount() { return dataList.size(); } protected abstract void bindData(GeneralWrapRecyclerHolder holder, T data); /** * 获取头部View数量 * * @return 头部View数量 */ private int getHeaderItemCount() { return headerViews.size(); } /** * 创建头部View或底部View的ViewHolder * * @param view 头部View或底部View * @return ViewHolder */ private GeneralWrapRecyclerHolder createHeaderFooterViewHolder(View view) { return new GeneralWrapRecyclerHolder(view) { }; } /** * 判断是否是头部View * * @param key Key * @return 是否是头部View */ private boolean isHeaderViewType(int key) { return headerViews.indexOfKey(key) > -1; } /** * 判断是否是底部View * * @param key Key * @return 是否是底部View */ private boolean isFooterViewType(int key) { return footerViews.indexOfKey(key) > -1; } /** * 根据索引判断该位置的View是否是头部View * * @param position 索引 * @return 是否是头部View */ private boolean isHeaderPosition(int position) { return (position > -1) && (position < headerViews.size()); } /** * 根据索引判断该位置的View是否是底部View * * @param position 索引 * @return 是否是底部View */ private boolean isFooterPosition(int position) { return (position >= (headerViews.size() + getDataItemCount())) && (position < (headerViews.size() + getDataItemCount() + footerViews.size())); } /** * 添加头部View * * @param view 头部View */ public void addHeaderView(View view) { if (headerViews.indexOfValue(view) < 0) { headerViews.put(BASE_ITEM_TYPE_HEADER++, view); notifyDataSetChanged(); } } /** * 添加底部View * * @param view 底部View */ public void addFooterView(View view) { if (footerViews.indexOfValue(view) < 0) { footerViews.put(BASE_ITEM_TYPE_FOOTER++, view); notifyDataSetChanged(); } } /** * 移除头部View * * @param view View */ public void removeHeaderView(View view) { int index = headerViews.indexOfValue(view); if (index > -1) { headerViews.removeAt(index); notifyDataSetChanged(); } } /** * 移除底部View * * @param view View */ public void removeFooterView(View view) { int index = footerViews.indexOfValue(view); if (index > -1) { footerViews.removeAt(index); notifyDataSetChanged(); } } } 

三、GeneralWrapRecyclerView

/** * 作者: 叶应是叶 * 时间: 2017/6/4 * 描述: 可以带头部View与尾部View的RecyclerView */ public class GeneralWrapRecyclerView extends RecyclerView { private GeneralWrapRecyclerAdapter mRecyclerAdapter; public GeneralWrapRecyclerView(Context context) { super(context); } public GeneralWrapRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public GeneralWrapRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setAdapter(GeneralWrapRecyclerAdapter recyclerAdapter) { this.mRecyclerAdapter = recyclerAdapter; super.setAdapter(recyclerAdapter); } @Override public void setAdapter(Adapter adapter) { } /** * 添加头部View * * @param view View */ public void addHeaderView(View view) { if (mRecyclerAdapter != null) { mRecyclerAdapter.addHeaderView(view); } else { throw new RuntimeException("GeneralRecyclerAdapter == null"); } } /** * 添加底部View * * @param view View */ public void addFooterView(View view) { if (mRecyclerAdapter != null) { mRecyclerAdapter.addFooterView(view); } else { throw new RuntimeException("GeneralRecyclerAdapter == null"); } } /** * 移除头部View * * @param view View */ public void removeHeaderView(View view) { if (mRecyclerAdapter != null) { mRecyclerAdapter.removeHeaderView(view); } else { throw new RuntimeException("GeneralRecyclerAdapter == null"); } } /** * 移除底部View * * @param view View */ public void removeFooterView(View view) { if (mRecyclerAdapter != null) { mRecyclerAdapter.removeFooterView(view); } else { throw new RuntimeException("GeneralRecyclerAdapter == null"); } } } 

四、实际使用

这里在为 GeneralWrapRecyclerView 设置 Adapter 时就只能将参数值设置为 GeneralWrapRecyclerAdapter 类的对象了

public class GeneralWrapRecyclerActivity extends AppCompatActivity implements GeneralWrapRecyclerHolder.onClickCommonListener, View.OnClickListener { private List<Data> dataList; private List<View> headerViewList; private List<View> footerViewList; private GeneralWrapRecyclerView generalWrapRecyclerView; private MyGeneralWrapRecyclerAdapter myGeneralWrapRecyclerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_general_wrap_recycler); initData(); generalWrapRecyclerView = (GeneralWrapRecyclerView) findViewById(R.id.rv_testRecyclerView); generalWrapRecyclerView.setLayoutManager(new LinearLayoutManager(this)); myGeneralWrapRecyclerAdapter = new MyGeneralWrapRecyclerAdapter(this, dataList, R.layout.item, this); generalWrapRecyclerView.setAdapter(myGeneralWrapRecyclerAdapter); View headerView1 = getLayoutInflater().inflate(R.layout.header_view, generalWrapRecyclerView, false); View headerView2 = getLayoutInflater().inflate(R.layout.header_view, generalWrapRecyclerView, false); View footerView1 = getLayoutInflater().inflate(R.layout.footer_view, generalWrapRecyclerView, false); View footerView2 = getLayoutInflater().inflate(R.layout.footer_view, generalWrapRecyclerView, false); generalWrapRecyclerView.addHeaderView(headerView1); generalWrapRecyclerView.addHeaderView(headerView2); generalWrapRecyclerView.addFooterView(footerView1); generalWrapRecyclerView.addFooterView(footerView2); headerViewList.add(headerView1); headerViewList.add(headerView2); footerViewList.add(footerView1); footerViewList.add(footerView2); findViewById(R.id.btn_addData).setOnClickListener(this); findViewById(R.id.btn_deleteData).setOnClickListener(this); findViewById(R.id.btn_addHeaderView).setOnClickListener(this); findViewById(R.id.btn_deleteHeaderView).setOnClickListener(this); findViewById(R.id.btn_addFooterView).setOnClickListener(this); findViewById(R.id.btn_deleteFooterView).setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_addData: Data data = new Data(R.mipmap.ic_launcher, "Hi"); dataList.add(data); myGeneralWrapRecyclerAdapter.notifyDataSetChanged(); break; case R.id.btn_deleteData: if (dataList.size() > 0) { dataList.remove(0); } myGeneralWrapRecyclerAdapter.notifyDataSetChanged(); break; case R.id.btn_addHeaderView: View headerView = getLayoutInflater().inflate(R.layout.header_view, generalWrapRecyclerView, false); headerViewList.add(headerView); generalWrapRecyclerView.addHeaderView(headerView); break; case R.id.btn_deleteHeaderView: if (headerViewList.size() > 0) { generalWrapRecyclerView.removeHeaderView(headerViewList.get(0)); headerViewList.remove(0); } break; case R.id.btn_addFooterView: View footerView = getLayoutInflater().inflate(R.layout.footer_view, generalWrapRecyclerView, false); footerViewList.add(footerView); generalWrapRecyclerView.addFooterView(footerView); break; case R.id.btn_deleteFooterView: if (footerViewList.size() > 0) { generalWrapRecyclerView.removeFooterView(footerViewList.get(0)); footerViewList.remove(0); } break; } } private void initData() { dataList = new ArrayList<>(); headerViewList = new ArrayList<>(); footerViewList = new ArrayList<>(); for (int i = 0; i < 50; i++) { Data data = new Data(R.mipmap.ic_launcher_round, "Hi:" + i); dataList.add(data); } } @Override public void onClick(int position) { Toast.makeText(this, "点击:" + position, Toast.LENGTH_SHORT).show(); } @Override public void onLongClick(int position) { Toast.makeText(this, "长按:" + position, Toast.LENGTH_SHORT).show(); } } 

运行效果:


img_5f589caaec8176370118b87993624545.gif
这里写图片描述

这里提供代码下载:解析RecyclerView(3)——以更简单的方法实现带顶部View和底部View的RecyclerView

原文链接:https://yq.aliyun.com/articles/649712
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章