打造android偷懒神器———RecyclerView的万能适配器
转载请注明出处谢谢:http://www.cnblogs.com/liushilin/p/5720926.html
很不好意思让大家久等了,本来昨天就应该写这个的,无奈公司昨天任务比较紧,所以没能按时给大家带来RecyclerView的适配器,楼主对期待的小伙伴表示最深刻地歉意。
如果你没有看前面的万能的ListView,GridView等的万能适配器,楼主推荐你去看一看,当然,大牛就免了。
另外,楼主今天在构思这个RecyclerView的过程中发现前天写的ListView有点毛病,现在楼主已经更改了,并且重新提交到了github,有需要的小伙伴自己去抓紧看吧。
这里是直通车:http://www.cnblogs.com/liushilin/p/5716306.html
RecyclerView也出来这么久了,虽不说大家都耳熟能详,但是至少还是很有影响力的,毕竟官方是推出来替代过往的ListView,GridView等列表显示控件的,自然有她神奇的地方。我们肯定得紧跟时代的步伐嘛。
对RecyclerView的简单使用还不了解的小伙伴,我推荐你去看一看我之前写的RecyclerView的简单使用,虽说可能不如大牛们写的面面俱到,全是精髓,但是我相信一定有它存在的意义。传送门:http://www.cnblogs.com/liushilin/p/5673833.html
简单上个运行图:
好了,大都不多说。直入今天的正题——偷懒神器,RecyclerView万能适配器的简单构造思路。
其实多半还是和之前构造ListView的万能适配器差不多哈,毕竟RecyclerView就是为了替代它们出现的,只是RecyclerView封装了ViewHolder而已,而我们要实现把ViewHolder和Adaper封装成一个万能的适配器,我们肯定还是得像上篇提到的利用每个view独一无二的id进行键值映射来做处理,当然我们还是用现在官方推荐的SparseArray,这个东西在能替代HashMap的时候真的好用,性能的优化就不用多说了。
先看看核心代码:
我们封装了ViewHolder,为了把设置值等都封装进去,我们对外提供了set方法。通过一个getView来实现之前类似于ViewHolder的设置标签的效果。如果已经绑定,则直接返回,否则放到SparseArray中。
下面是ViewHolder的基本封装。如果你有之前ListView的ViewHolder的封装,这个看起来我相信很好理解。
1 package com.example.nanchen.commonadaperrecyclerdemo; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.support.v7.widget.RecyclerView; 6 import android.util.SparseArray; 7 import android.view.View; 8 import android.widget.ImageView; 9 import android.widget.TextView; 10 11 import com.squareup.picasso.Picasso; 12 13 /** 14 * 万能的RecyclerView的ViewHolder 15 * Created by 南尘 on 16-7-30. 16 */ 17 public class BaseRecyclerHolder extends RecyclerView.ViewHolder { 18 19 private SparseArray<View> views; 20 private Context context; 21 22 private BaseRecyclerHolder(Context context,View itemView) { 23 super(itemView); 24 this.context = context; 25 //指定一个初始为8 26 views = new SparseArray<>(8); 27 } 28 29 /** 30 * 取得一个RecyclerHolder对象 31 * @param context 上下文 32 * @param itemView 子项 33 * @return 返回一个RecyclerHolder对象 34 */ 35 public static BaseRecyclerHolder getRecyclerHolder(Context context,View itemView){ 36 return new BaseRecyclerHolder(context,itemView); 37 } 38 39 public SparseArray<View> getViews(){ 40 return this.views; 41 } 42 43 /** 44 * 通过view的id获取对应的控件,如果没有则加入views中 45 * @param viewId 控件的id 46 * @return 返回一个控件 47 */ 48 @SuppressWarnings("unchecked") 49 public <T extends View> T getView(int viewId){ 50 View view = views.get(viewId); 51 if (view == null ){ 52 view = itemView.findViewById(viewId); 53 views.put(viewId,view); 54 } 55 return (T) view; 56 } 57 58 /** 59 * 设置字符串 60 */ 61 public BaseRecyclerHolder setText(int viewId,String text){ 62 TextView tv = getView(viewId); 63 tv.setText(text); 64 return this; 65 } 66 67 /** 68 * 设置图片 69 */ 70 public BaseRecyclerHolder setImageResource(int viewId,int drawableId){ 71 ImageView iv = getView(viewId); 72 iv.setImageResource(drawableId); 73 return this; 74 } 75 76 /** 77 * 设置图片 78 */ 79 public BaseRecyclerHolder setImageBitmap(int viewId, Bitmap bitmap){ 80 ImageView iv = getView(viewId); 81 iv.setImageBitmap(bitmap); 82 return this; 83 } 84 85 /** 86 * 设置图片 87 */ 88 public BaseRecyclerHolder setImageByUrl(int viewId,String url){ 89 Picasso.with(context).load(url).into((ImageView) getView(viewId)); 90 // ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context)); 91 // ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId)); 92 return this; 93 } 94 }
然后是Recycler的Adapter,由于RecyclerView的Adapter必须继承自RecyclerView.Adapter,并且指定我们写的ViewHolder为泛型,为了达到万能的效果,我们把需要传入的Java Bean属性直接用一个泛型T指代。
下面这些值得你注意:
1)RecyclerView没有提供Item的点击事件,所以我们需要自己自定义,建议实现在Adapter中,因为adapter里面会用到ViewHolder,这样有助用我们写每一项的点击事件。
2)RecyclerView不仅支持全局刷新,而且支持局部刷新,所以我们建议把添加和删除的方法直接写在Adapter中。
3)我们为了达到万能的效果,所以我们把设置holder的方法作为一个抽象方法,方面我们通过viewId传值到相应的控件中,把整个Adapter变成一个抽象方法,这样在子类中就可以去通过强制实现的方式把我们的数据填充进去。
还是直接看源码吧。
1 package com.example.nanchen.commonadaperrecyclerdemo; 2 3 import android.content.Context; 4 import android.support.v7.widget.RecyclerView; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 9 import java.util.List; 10 11 /** 12 * 万能的RecyclerView适配器 13 * Created by 南尘 on 16-7-30. 14 */ 15 public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<BaseRecyclerHolder> { 16 17 private Context context;//上下文 18 private List<T> list;//数据源 19 private LayoutInflater inflater;//布局器 20 private int itemLayoutId;//布局id 21 private boolean isScrolling;//是否在滚动 22 private OnItemClickListener listener;//点击事件监听器 23 private OnItemLongClickListener longClickListener;//长按监听器 24 private RecyclerView recyclerView; 25 26 //在RecyclerView提供数据的时候调用 27 @Override 28 public void onAttachedToRecyclerView(RecyclerView recyclerView) { 29 super.onAttachedToRecyclerView(recyclerView); 30 this.recyclerView = recyclerView; 31 } 32 33 @Override 34 public void onDetachedFromRecyclerView(RecyclerView recyclerView) { 35 super.onDetachedFromRecyclerView(recyclerView); 36 this.recyclerView = null; 37 } 38 39 /** 40 * 定义一个点击事件接口回调 41 */ 42 public interface OnItemClickListener { 43 void onItemClick(RecyclerView parent, View view, int position); 44 } 45 46 public interface OnItemLongClickListener { 47 boolean onItemLongClick(RecyclerView parent, View view, int position); 48 } 49 50 /** 51 * 插入一项 52 * 53 * @param item 54 * @param position 55 */ 56 public void insert(T item, int position) { 57 list.add(position, item); 58 notifyItemInserted(position); 59 } 60 61 /** 62 * 删除一项 63 * 64 * @param position 删除位置 65 */ 66 public void delete(int position) { 67 list.remove(position); 68 notifyItemRemoved(position); 69 } 70 71 public BaseRecyclerAdapter(Context context, List<T> list, int itemLayoutId) { 72 this.context = context; 73 this.list = list; 74 this.itemLayoutId = itemLayoutId; 75 inflater = LayoutInflater.from(context); 76 77 // recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 78 // @Override 79 // public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 80 // super.onScrollStateChanged(recyclerView, newState); 81 // isScrolling = !(newState == RecyclerView.SCROLL_STATE_IDLE); 82 // if (!isScrolling) { 83 // notifyDataSetChanged(); 84 // } 85 // } 86 // }); 87 } 88 89 @Override 90 public BaseRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) { 91 View view = inflater.inflate(itemLayoutId, parent, false); 92 return BaseRecyclerHolder.getRecyclerHolder(context, view); 93 } 94 95 @Override 96 public void onBindViewHolder(final BaseRecyclerHolder holder, int position) { 97 98 if (listener != null){ 99 holder.itemView.setBackgroundResource(R.drawable.recycler_bg);//设置背景 100 } 101 holder.itemView.setOnClickListener(new View.OnClickListener() { 102 @Override 103 public void onClick(View view) { 104 if (listener != null && view != null && recyclerView != null) { 105 int position = recyclerView.getChildAdapterPosition(view); 106 listener.onItemClick(recyclerView, view, position); 107 } 108 } 109 }); 110 111 112 holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 113 @Override 114 public boolean onLongClick(View view) { 115 if (longClickListener != null && view != null && recyclerView != null) { 116 int position = recyclerView.getChildAdapterPosition(view); 117 longClickListener.onItemLongClick(recyclerView, view, position); 118 return true; 119 } 120 return false; 121 } 122 }); 123 124 convert(holder, list.get(position), position, isScrolling); 125 126 } 127 128 @Override 129 public int getItemCount() { 130 return list == null ? 0 : list.size(); 131 } 132 133 public void setOnItemClickListener(OnItemClickListener listener) { 134 this.listener = listener; 135 } 136 137 public void setOnItemLongClickListener(OnItemLongClickListener longClickListener) { 138 this.longClickListener = longClickListener; 139 } 140 141 /** 142 * 填充RecyclerView适配器的方法,子类需要重写 143 * 144 * @param holder ViewHolder 145 * @param item 子项 146 * @param position 位置 147 * @param isScrolling 是否在滑动 148 */ 149 public abstract void convert(BaseRecyclerHolder holder, T item, int position, boolean isScrolling); 150 }
注意其中我的抽象方法给了一个isScrolling的参数,我的目的是想控制滑动的时候不加载图片。目前这个还没实现,所以大家可以在自己封装的时候不去写它,当然,你有思考的话我建议大家最好实现吧,另外别忘了告诉楼主哦~~嘿嘿。楼主就是这样的谦(zhuang)虚(bi)。
其他的代码就很简单了,java bean类Data和布局和昨天一样的,大家可以自己去随便怎么布局。
这里只上一个MainActivity的代码,有需要的大家可以去github提取:https://github.com/nanchen2251/CommonAdapterRecyclerDemo
package com.example.nanchen.commonadaperrecyclerdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.EditText; import android.widget.Toast; import java.util.ArrayList; import java.util.List; import java.util.Locale; public class MainActivity extends AppCompatActivity { private List<Data> list; private RecyclerView recyclerView; private BaseRecyclerAdapter<Data> adapter; private EditText text; @SuppressWarnings("unchecked") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); list = new ArrayList<>(); initList(); adapter = new BaseRecyclerAdapter<Data>(this,list,R.layout.list_item) { @Override public void convert(BaseRecyclerHolder holder, Data item, int position, boolean isScrolling) { holder.setText(R.id.item_text,item.getText()); if (item.getImageUrl() != null){ holder.setImageByUrl(R.id.item_image,item.getImageUrl()); }else { holder.setImageResource(R.id.item_image,item.getImageId()); } } }; adapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() { @Override public void onItemClick(RecyclerView parent, final View view, int position) { Toast.makeText(MainActivity.this, String.format(Locale.CHINA,"你点击了第%d项,长按会删除!",position),Toast.LENGTH_SHORT).show(); } }); adapter.setOnItemLongClickListener(new BaseRecyclerAdapter.OnItemLongClickListener() { @Override public boolean onItemLongClick(RecyclerView parent, View view, int position) { adapter.delete(position); return true; } }); text = (EditText) findViewById(R.id.main_text); recyclerView = (RecyclerView) findViewById(R.id.main_recycler); recyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)); recyclerView.setAdapter(adapter); } public void initList(){ for (int i = 0; i < 5; i++) { list.add(new Data("本地 "+i,R.mipmap.ic_launcher)); } for (int i = 0; i < 5; i++) { list.add(new Data("网络 "+i,"http://pic.cnblogs.com/face/845964/20160301162812.png")); } } public void btnClick(View view) { String string = text.getText().toString().trim(); Data data = new Data(string,R.mipmap.ic_launcher); // list.add(list.size()/2,data); adapter.insert(data,list.size()/2); Toast.makeText(MainActivity.this,list.size()+"",Toast.LENGTH_SHORT).show(); } }
作 者:
南 尘
出 处: http://www.cnblogs.com/liushilin/
关于作者:专注于移动前端的项目开发。如有问题或建议,请多多赐教!欢迎加入Android交流群:118116509
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章下部【推荐】或侧边【关注】。您的鼓励是作者坚持原创和持续写作的最大动力!
欢迎关注我的公众号,精讲面试、算法、Andrid、Java、Python,旨在打造全网最比心的公众号。

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?