前言
我们大家都知道ListView具有添加头部和添加底部的方法,但是RecyclerView并没有这样子的方法。所以RecyclerView是不能添加底部和头部的,但是能不能仿造ListView来实现RecyclerView添加头部和底部呢?答案当然是可行的。本文就来给大家介绍了关于Android封装RecyclerView添加头部和底部的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
首先看下实现的效果:
代码如下:
public class WrapMyRecyclerView extends RecyclerView { private Adapter mAdapter; private ArrayList<View> mHeaderViewInfos = new ArrayList<>();//保存headerView private ArrayList<View> mFooterViewInfos = new ArrayList<>();//保存footerView public WrapMyRecyclerView(Context context) { super(context); } public WrapMyRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public WrapMyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * 添加headerView * @param v */ public void addHeaderView(View v) { mHeaderViewInfos.add(v); // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof RecyclerHeaderViewListAdapter)) { wrapHeaderListAdapterInternal(); } } } /** * 添加一个footerView * @param v */ public void addFooterView(View v) { mFooterViewInfos.add(v); // Wrap the adapter if it wasn't already wrapped. if (mAdapter != null) { if (!(mAdapter instanceof RecyclerHeaderViewListAdapter)) { wrapHeaderListAdapterInternal(); } } } /** * 设置一个Adapter * @param adapter */ @Override public void setAdapter(Adapter adapter) { if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) { mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } super.setAdapter(mAdapter); } private void wrapHeaderListAdapterInternal() { mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter); } /** * 新建一个RecyclerHeaderViewListAdapter对象 * 最终的adapter实现它 * @param headerViewInfos * @param footerViewInfos * @param adapter * @return */ protected RecyclerHeaderViewListAdapter wrapHeaderListAdapterInternal( ArrayList<View> headerViewInfos, ArrayList<View> footerViewInfos, Adapter adapter) { return new RecyclerHeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter); } }
这就是封装的RecyclerView,里面主要有三个方法addHeaderView、 addFooterView和重写的setAdapter。这里的唯一的思想就是偷梁换柱,当我们添加头部、尾部或者设置adapter时,真正的adapter并不是我们传入的adapter,而是重新new 了一个RecyclerHeaderViewListAdapter。这才是RecyclerView最终设置的adapter。
其实我们看到ListView也是通过这样子的思想来添加头部和尾部的。
这就是ListView的addHeaderView方法,它会偷偷的创建HeaderViewListAdapter这个adapter。最终添加的header和footer在HeaderViewListAdapter里面实现。
public class RecyclerHeaderViewListAdapter extends RecyclerView.Adapter { private final ArrayList<View> mHeaderViewInfos;//保存headerview数据 private final ArrayList<View> mFooterViewInfos;//保存footerView数据 private RecyclerView.Adapter mAdapter; //用户自己构造的adapter private static final int RECYCLER_HEADER_VIEW = 0x001;//headerview类型 private static final int RECYCLER_FOOTER_VIEW = 0x002;//footerView类型 /** * 构造方法 * 初始化 * @param headerViewInfos * @param footerViewInfos * @param adapter */ public RecyclerHeaderViewListAdapter(ArrayList<View> headerViewInfos, ArrayList<View> footerViewInfos, RecyclerView.Adapter adapter) { mAdapter = adapter; if (headerViewInfos == null) { mHeaderViewInfos = new ArrayList<>(); } else { mHeaderViewInfos = headerViewInfos; } if (footerViewInfos == null) { mFooterViewInfos = new ArrayList<>(); } else { mFooterViewInfos = footerViewInfos; } } /** * 根据getItemViewType返回的条目类型 * 创建不同的itemview * 传入的adapter,回调它的onCreateViewHolder即可 * @param parent * @param viewType * @return */ @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == RECYCLER_HEADER_VIEW){ return new HeaderViewLayout(mHeaderViewInfos.get(0)); }else if (viewType == RECYCLER_FOOTER_VIEW){ return new HeaderViewLayout(mFooterViewInfos.get(0)); } return mAdapter.onCreateViewHolder(parent,viewType); } /** * 绑定数据 * headerview和footerview不需要绑定数据,直接return即可 * 传入的adapter需要回调它的onBindViewHolder即可 * @param holder * @param position */ @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { //header int numHeaders = getHeadersCount(); if (position < numHeaders) { return ; } //adapter body final int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { mAdapter.onBindViewHolder(holder, adjPosition); return ; } } //footer } /** * 返回条目的类型 * 传入的adapter,回调它的getItemViewType即可 * @param position * @return */ @Override public int getItemViewType(int position) { // Header int numHeaders = getHeadersCount(); if (position < numHeaders) { return RECYCLER_HEADER_VIEW; } // Adapter final int adjPosition = position - numHeaders; int adapterCount = 0; if (mAdapter != null) { adapterCount = mAdapter.getItemCount(); if (adjPosition < adapterCount) { return mAdapter.getItemViewType(position); } } // Footer (off-limits positions will throw an IndexOutOfBoundsException) return RECYCLER_FOOTER_VIEW; } /** * 总条目即:footerview的条目+headerview的条目+穿入的adapter条目 * @return */ @Override public int getItemCount() { if (mAdapter != null) { return getFootersCount() + getHeadersCount() + mAdapter.getItemCount(); } else { return getFootersCount() + getHeadersCount(); } } /** * 获取headerview的条目 * @return */ public int getHeadersCount() { return mHeaderViewInfos.size(); } /** * 获取footervie的条目 * @return */ public int getFootersCount() { return mFooterViewInfos.size(); } /** * 这是footerview和headerview的ViewHolder需要 * 这里只是提供一个构造器即可,实际上用处不大 */ private static class HeaderViewLayout extends RecyclerView.ViewHolder{ public HeaderViewLayout(View itemView) { super(itemView); } } }
这是实现添加header、footer和传入adapter的RecyclerHeaderViewListAdapter。具体的逻辑都在文件的注释里面有说明。逻辑是仿造ListView的HeaderViewListAdapter来实现的。
其实就是创建一个adapter,然后根据不同的条目类型来创建条目和绑定条目的数据即可。
<?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:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.wrap.recycler.WrapRecyclerViewActivity"> <com.lwj.wrap.recycler.WrapMyRecyclerView android:id="@+id/wrap_recyclerview" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
这是布局文件
public class WrapRecyclerViewActivity extends AppCompatActivity { private WrapMyRecyclerView mRecyclerView; private MyAdapter mMyAdapter; private List<String> mList01 = new ArrayList<>(); private static final int WC = ViewGroup.LayoutParams.WRAP_CONTENT; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wrap_recycler_view); getData(); mRecyclerView = (WrapMyRecyclerView) this.findViewById(R.id.wrap_recyclerview); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); //加入headerView ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(WC,WC); ImageView headerView = new ImageView(this); headerView.setImageResource(R.drawable.timg); headerView.setLayoutParams(params); mRecyclerView.addHeaderView(headerView); //设置adapter mMyAdapter = new MyAdapter(this,mList01); mRecyclerView.setAdapter(mMyAdapter); //加入footerView params = new ViewGroup.LayoutParams(WC,WC); ImageView footerView = new ImageView(this); footerView.setImageResource(R.drawable.hhfj); footerView.setLayoutParams(params); mRecyclerView.addFooterView(footerView); mMyAdapter.notifyDataSetChanged(); } public void getData() { for (int i = 0; i < 3; i++) { String data ="adapter...." + i; mList01.add(data); } } }
使用直接调用addHeaderView、addFooterView即可。
除了这种方式来实现addHeaderView和addFooterView,另外一种方式就是封装Adapter来实现,原理还是保持不变:根据不同的条目类型来创建条目和绑定条目的数据。
public class MyCircleRecycViewAdapter extends RecyclerView.Adapter { public List<CircleInfo.CirclePageInfo> infos = null; private Context mContext; private ListViewImgLoader mLoader; private View VIEW_FOOTER;//尾部 private View VIEW_HEADER;//头部 //Type private int TYPE_NORMAL = 1000; private int TYPE_HEADER = 1001; private int TYPE_FOOTER = 1002; private int tagType = TYPE_NORMAL; public MyCircleRecycViewAdapter(Context context,List<CircleInfo.CirclePageInfo> datas) { this.infos = datas; this.mContext = context; mLoader = new ListViewImgLoader(); mLoader.setMemoryCacheSize(1024 * 1024); mLoader.setVisibleItemCount(12); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_FOOTER) { tagType = TYPE_FOOTER; return new MyCircleItemHolder(VIEW_FOOTER); } else if (viewType == TYPE_HEADER) { tagType = TYPE_HEADER; return new MyCircleItemHolder(VIEW_HEADER); } else { tagType = TYPE_NORMAL; View view = LayoutInflater.from(mContext).inflate(R.layout.circle_gridview_items, parent,false); return new MyCircleItemHolder(view); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (!isHeaderView(position) && !isFooterView(position)) { if (haveHeaderView()) position--; MyCircleItemHolder viewHolder = (MyCircleItemHolder)holder; CircleInfo.CirclePageInfo mInfo = infos.get(position); setData(viewHolder,mInfo); } } @Override public int getItemCount() { int count = (infos == null ? 0 : infos.size()); if (VIEW_FOOTER != null) { count++; } if (VIEW_HEADER != null) { count++; } return count; } @Override public int getItemViewType(int position) { if (isHeaderView(position)) { return TYPE_HEADER; } else if (isFooterView(position)) { return TYPE_FOOTER; } else { return TYPE_NORMAL; } } public void addHeaderView(View headerView) { if (haveHeaderView()) { throw new IllegalStateException("hearview has already exists!"); } else { //避免出现宽度自适应 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(Utils.getRealPixel(30), Utils.getRealPixel(230)); headerView.setLayoutParams(params); VIEW_HEADER = headerView; notifyItemInserted(0); } } public void addFooterView(View footerView) { if (haveFooterView()) { throw new IllegalStateException("footerView has already exists!"); } else { ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); footerView.setLayoutParams(params); VIEW_FOOTER = footerView; // ifGridLayoutManager(); notifyItemInserted(getItemCount() - 1); } } private boolean haveHeaderView() { return VIEW_HEADER != null; } public boolean haveFooterView() { return VIEW_FOOTER != null; } private boolean isHeaderView(int position) { return haveHeaderView() && position == 0; } private boolean isFooterView(int position) { return haveFooterView() && position == getItemCount() - 1; } private void setData(final MyCircleItemHolder viewHolder, final CircleInfo.CirclePageInfo mInfo) { if(mInfo == null || mInfo == viewHolder.tagInfo) { return; }else{ viewHolder.tagInfo = mInfo; if(!StrUtils.isEnpty(mInfo.thread_unread)){ if(mInfo.thread_unread.equals("0")){ viewHolder.threaduUnreadTv.setVisibility(View.INVISIBLE); }else{ viewHolder.threaduUnreadTv.setVisibility(View.VISIBLE); viewHolder.threaduUnreadTv.setText(mInfo.thread_unread); } }else{ viewHolder.threaduUnreadTv.setVisibility(View.INVISIBLE); } if (!TextUtils.isEmpty(viewHolder.tagUrl)){ if(mInfo.circle_img_path != null && !(mInfo.circle_img_path.equals(viewHolder.tagUrl))){ setImage(viewHolder.mImageView,mInfo.circle_img_path); } }else{ setImage(viewHolder.mImageView,mInfo.circle_img_path); } if (!TextUtils.isEmpty(mInfo.circle_img_path)){ viewHolder.tagUrl = mInfo.circle_img_path; } if(mInfo.circleName != null){ if(mInfo.circleName.length() > 5){ String s = mInfo.circleName.substring(0,5) + "..."; viewHolder.textView.setText(s); }else{ viewHolder.textView.setText(mInfo.circleName); } } } } private void setImage(final ColorFilterImageView mImageView, final String imgUrl){ mImageView.setBackgroundColor(0xffadadad); mImageView.setImageBitmap(null); if(!TextUtils.isEmpty(imgUrl)) { mLoader.loadImage(mImageView.hashCode(), imgUrl, 300, new DnImg.OnDnImgListener() { @Override public void onProgress(String url, int downloadedSize, int totalSize) { // TODO Auto-generated method stub } @Override public void onFinish(String url, String file, Bitmap bmp) { if(url.equals(imgUrl)) { mImageView.setImageBitmap(bmp); } } }); }else{ mImageView.setBackgroundColor(0xffadadad); mImageView.setImageBitmap(null); } } class MyCircleItemHolder extends RecyclerView.ViewHolder{ private ColorFilterImageView mImageView; private TextView textView; private CircleInfo.CirclePageInfo tagInfo; private String tagUrl; private TextView threaduUnreadTv; public MyCircleItemHolder(final View itemView) { super(itemView); if(tagType == TYPE_NORMAL){ mImageView = (ColorFilterImageView)itemView.findViewById(R.id.quan_icon); mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); textView = (TextView)itemView.findViewById(R.id.quan_name); threaduUnreadTv = (TextView)itemView.findViewById(R.id.quan_num); mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mListener != null){ mListener.oncliItem(itemView,tagInfo,threaduUnreadTv); } } }); } } } public OnclickMyCircleItemListener mListener; public void setOnclickMyCircleItemListener(OnclickMyCircleItemListener l){ this.mListener = l; } public interface OnclickMyCircleItemListener{ void oncliItem(View view,CircleInfo.CirclePageInfo info,View threadNumTv); } public void pauseLoader(){ if(mLoader != null) { mLoader.pause(); } } public void resumeLoader(){ if(mLoader != null) { mLoader.resume(); } } public void closeLoader(){ if(mLoader != null) { mLoader.close(); } } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对各位Android开发者们的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对呐喊教程的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。