前言
我们大家都知道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开发者们的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。
本文向大家介绍Android底部菜单栏实现的实例代码,包括了Android底部菜单栏实现的实例代码的使用技巧和注意事项,需要的朋友参考一下 Android 使用RadioGroup 实现底部导航菜单栏。 一、主界面布局的实现: 先来张效果图: 介绍一下总体界面包括的内容:底部五个导航按钮,主界面包括一个FrameLayout用来放五个Fragment。点击底部按钮会对应跳转到指定的界面。 实现布
主要内容:ion-header-bar,ion-footer-barion-header-bar 这个是固定在屏幕顶部的一个头部标题栏。如果给它加上'bar-subheader' 这个样式,它就是副标题。 用法 API 属性 类型 描述 align-title (optional) 这个是对齐 title 的。如果没有设置,它将会按照手机的默认排版(Ios的默认是居中,Android默认是居左)。它的值可以是 'left','center','right'。 no
ion-header-bar 这个是固定在屏幕顶部的一个头部标题栏。如果给它加上'bar-subheader' 这个样式,它就是副标题。 用法 <ion-header-bar align-title="left"> <div> <button ng-click="doSomething()">Left Button</button> </div> <h1>Title!</h1
本文向大家介绍android中RecycleView添加下滑到底部的监听示例,包括了android中RecycleView添加下滑到底部的监听示例的使用技巧和注意事项,需要的朋友参考一下 我们的日常开发中经常用到下拉刷新,而网上评价最好的开源下拉刷新组件当然还是android-Ultra-Pull-To-Refresh 此组件可以给任何的控件添加下拉刷新功能。当然也包括recycleview了。
本文向大家介绍Android实现从底部弹出的Dialog示例(一),包括了Android实现从底部弹出的Dialog示例(一)的使用技巧和注意事项,需要的朋友参考一下 一.概述 先给大家看一下效果图: 点击中间的显示弹框按钮,从底部弹出来一个对话框,用户可以点击拍照或者从相册选择进行相应的操作,下面看看怎么实现。 二.代码实现 主页面布局文件,很简单,一个按钮,响应点击事件: 接下来看对话框的布局
本文向大家介绍Maven添加Tomcat插件实现热部署代码实例,包括了Maven添加Tomcat插件实现热部署代码实例的使用技巧和注意事项,需要的朋友参考一下 Maven热部署,顾名思义就是可以不影响项目在服务器中的运行情况,可以实现项目代码的更新,减少启动,编译时间,达到快速开发的目的,也不需要手动拷贝war包到远程项目,可以直接将项目以及war包部署到远程服务器。 实现Maven热部署主要需要