RecycleView出来很长一段时间了,我也终于熟练使用了,所以决定写一个日志,用来总结并分享一下自己的理解。因为个人能力有限,写的比较浅显,所以命名为”入门“。如果有错误,欢迎指正。
组件 RecycleView 作为一个容器类的View,并不像 TextView 或者 ImageView 一样是独立个体。RecycleView 更像是一套框架。所以这个段落我命名为组件。 从功能和角色分工上,我把 RecycleView 分为4个部分,(仅代表个人观点,如果与官方文档有出入,请以官方文档为主)。这四个部分分别是: RecycleView 、 Adapter 、 LayoutManager 、 ViewHolder 。 他们有着明确的分工:
组件
工作
RecycleView
主体,负责ViewHolder的回收和复用,Adapter与LayoutManager的协调调度
Adapter
适配器,负责ViewHolder的创建与数据绑定
LayoutManager
布局管理器,负责排版与交互
ViewHolder
Item个体,负责单个ItemView的维护与状态管理
RecycleView 是主体,并且只负责回收复用Item
,根据Adapter
和LayoutManager
的反馈,协调几个组件,不包含任何业务逻辑。那么我们可以得到一个结论:RecycleView
不会干涉展示效果,也不会限制展示效果,只是处理复用逻辑。
Adapter 是适配器,只负责创建ViewHolder
和数据
绑定。那么我们可以得到一个结论:Adapter
不会干涉展示效果,但是会干涉数据内容、展示数据,决定创建的ViewHolder
类型。
LayoutManager
是布局管理器,只负责ItemView
的排版,不关联Adapter
和ViewHolder
。那么我们可以得到一个结论:LayoutManager
决定最终显示效果,Child
的布局方式,甚至交互效果;但是不能干涉View
的创建以及数据内容,不能决定View
的类型,也不能“看见”ViewHolder
。
ViewHolder
是ItemView的包装体,包含并记录View的状态、类型等属性,可以在其他三大组件间传递。我们可以总结为:它是Item个体的体现,负责对View
进行数据填充,以及细节逻辑处理。
从上面的整理,可以理解为:RecycleView是指挥者,负责协调调度;Adapter是生产者,负责产出有效的“产品”(RecycleView复用的ViewHolde仍然需要重新经过Adapter的数据绑定,直接展示的情况是产品可用,没有被回收);LayoutManager是消费者,负责使用产出成品和抛弃无用产品;ViewHolder是产品,或者理解为包裹,就像快递包裹一样,统一的包装,标明里面的内容,方便保存,传递,分类,使用。
优点 那么这么做的优点是什么呢?因为很多初学者朋友都在说,还没有ListView简单,好复杂啊。 其实并不这样的,可以片面的理解他为ListView的升级版,将ListView和GridView合并起来了,大家可曾还记得那些奇奇怪怪的需求?
横向滚动的列表
横向滚动的宫格
横向瀑布流
纵向瀑布流
Banner图
卡片堆叠
画廊
谷歌肯定也遇到了这些需求,他们前期也有为这些需求做过一些偏门的实现,有兴趣的小伙伴可以去搜索一下。不过都因为扩展性太差而被冷落了。那么有没有办法一劳永逸呢?有的,所以他们做出了RecycleView
。通过第一节的分析,想必很多小伙伴已经看出来了,或者对ListView
和RecycleView
都比较熟悉的小伙伴也看出来了。RecycleView
其实是对ListView
结构的进一步抽象。
Adapter
其实没变,只是从原本的View
,增加了ViewHower
的封装。
LayoutManager
只是把ListView
和GridView
原本的排版工作独立起来。
ViewHolder
其实就是原本Adapter
创建的View
。
因为抽象出来了,所以干脆把原本Adapter
里面的getItemType
方法强化了一下。所以就改头换面了,然而其实并没有变,还是熟悉的味道,熟悉的配方。
使用 下面我们就使用RecyclerView来找找ListView的感觉:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 private void initView (RecyclerView recyclerView) { recyclerView.setLayoutManager(new LinearLayoutManager(this )); List<Bean> data = new ArrayList<>(); MyAdapter adapter = new MyAdapter(data); recyclerView.setAdapter(adapter); adapter.notifyDataSetChanged(); } private class MyAdapter extends RecyclerView .Adapter <MyHolder > { private List<Bean> data; public MyAdapter (List<Bean> data) { this .data = data; } @NonNull @Override public MyHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { return new MyHolder( LayoutInflater.from(parent.getContext()).inflate(R.layout.xxxxx,parent,false )); } @Override public void onBindViewHolder (@NonNull MyHolder holder, int position) { Bean bean = data.get(position); ((TextView)holder.itemView.findViewById(R.id.xxxx)).setText(bean.name); ((TextView)holder.itemView.findViewById(R.id.xxxx)).setText(bean.age); } @Override public int getItemCount () { return data.size(); } } private class Bean { String name = "" ; String age = "" ; } private class MyHolder extends RecyclerView .ViewHolder { public MyHolder (View itemView) { super (itemView); } }
以上,就是一个ListView风格的写法,是不是很熟悉,虽然看起来很乱,但是这确实可以用,也确实是ListView的写法,不是吗?而且很多入门Demo的写法也都是这样写的。 但是作为一个比较作的人,觉得不够优雅,一定要改改,所以我这样写,我称为“RecyclerView风格”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 private void initView (RecyclerView recyclerView) { recyclerView.setLayoutManager(new LinearLayoutManager(this )); List<Bean> data = new ArrayList<>(); MyAdapter adapter = new MyAdapter(this ,data); recyclerView.setAdapter(adapter); adapter.notifyDataSetChanged(); } private class MyAdapter extends RecyclerView .Adapter <MyHolder > { private List<Bean> data; private LayoutInflater inflater; public MyAdapter (Context context,List<Bean> data) { this .data = data; this .inflater = LayoutInflater.from(context); } @NonNull @Override public MyHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { return MyHolder.create(inflater,parent); } @Override public void onBindViewHolder (@NonNull MyHolder holder, int position) { holder.onBind(data.get(position)); } @Override public int getItemCount () { return data.size(); } } private class Bean { String name = "" ; String age = "" ; } private static class MyHolder extends RecyclerView .ViewHolder { static MyHolder create (LayoutInflater inflater,ViewGroup parent) { return MyHolder(inflater.inflate(R.layout.xxxx,parent,false )); } private TextView nameView; private TextView ageView; private MyHolder (View itemView) { super (itemView); nameView = itemView.findViewById(R.id.xxxx); ageView = itemView.findViewById(R.id.xxxx); } public void onBind (Bean bean) { nameView.setText(bean.name); ageView.setText(bean.age); } }
看到这里,有些比较较真的朋友就一定会有很多问题,我来一组QA吧!
问:这有区别吗? 答:没区别啊! 问:优点是什么? 答:好看! 问:性能有优化吗? 答:没有! 问:那么这有写,最大的好处是什么? 答:好看而已,没别的了。
是的,这套写法仅仅是为了好看,让自己看了舒服,让别人看了也舒服。你可能会说:也没好看到哪里去啊!那我把业务复杂一下,就可以看出哪里好看了。因为代码比较多了,所以很多地方我能简略的都尽量简略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 private void initView (RecyclerView recyclerView) { recyclerView.setLayoutManager(new LinearLayoutManager(this )); List<BaseBean> data = new ArrayList<>(); MyAdapter adapter = new MyAdapter(this ,data); recyclerView.setAdapter(adapter); adapter.addClickListener(new OnHolderClickListener() { @Override public void onHolderClick (BaseHolder holder, View view) { int postion = holder.getAdapterPosition(); if (holder instanceof MyHolder1){ if (view == holder.nameView){ }else if (view == holder.ageView){ } }else if (holder instanceof MyHolder2){ }else if (holder instanceof MyHolder3){ } } }); adapter.notifyDataSetChanged(); } private class MyAdapter extends RecyclerView .Adapter <BaseHolder > implements OnHolderClickListener { private List<BaseBean> data; private LayoutInflater inflater; private ArrayList<OnHolderClickListener> listeners = new ArrayList<>(); public MyAdapter (Context context,List<BaseBean> data) { this .data = data; this .inflater = LayoutInflater.from(context); } public void addClickListener (OnHolderClickListener listener) { this .listeners.add(listener); } public void removeClickListener (OnHolderClickListener listener) { this .listeners.remove(listener); } @NonNull @Override public BaseHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { BaseHolder holder; switch (viewType){ case 1 : { holder = MyHolder1.create(inflater,parent); break ; } case 2 : { holder = MyHolder2.create(inflater,parent); break ; } case 3 : { holder = MyHolder3.create(inflater,parent); break ; } default : { holder = null ; } } if (holder == null ){ throw new RuntimeException("未定义的viewType" ); } holder.setClickListener(this ); return holder; } @Override public void onBindViewHolder (@NonNull BaseHolder holder, int position) { holder.onBind(data.get(position)); } @Override public int getItemCount () { return data.size(); } @Override public int getItemViewType (int position) { return data.get(position).type; } @Override public void onHolderClick (BaseHolder holder, View view) { for (OnHolderClickListener listener : listeners){ if (listener != null ){ listener.onHolderClick(holder, view); } } } } private class BaseBean { int type = 0 ; } private class Bean1 extends BaseBean { public Bean1 () { type = 1 ; } } private class Bean2 extends BaseBean { public Bean2 () { type = 2 ; } } private class Bean3 extends BaseBean { public Bean3 () { type = 3 ; } } private static class BaseHolder extends RecyclerView .ViewHolder implements View .OnClickListener { private OnHolderClickListener clickListener; public void setClickListener (OnHolderClickListener listener) { this .clickListener = listener; } private BaseHolder (View itemView) { super (itemView); } public void onBind (BaseBean bean) { } @Override public void onClick (View v) { if (this .clickListener != null ){ this .clickListener.onHolderClick(this ,v); } } } private static class MyHolder1 extends BaseHolder { static MyHolder1 create (LayoutInflater inflater,ViewGroup parent) { return MyHolder1(inflater.inflate(R.layout.xxxx,parent,false )); } private MyHolder1 (View itemView) { super (itemView); } @Override public void onBind (BaseBean bean) { Bean1 data = (Bean1)bean; } } private static class MyHolder2 extends BaseHolder { static MyHolder2 create (LayoutInflater inflater,ViewGroup parent) { return MyHolder2(inflater.inflate(R.layout.xxxx,parent,false )); } private MyHolder2 (View itemView) { super (itemView); } @Override public void onBind (BaseBean bean) { } } private static class MyHolder3 extends BaseHolder { static MyHolder3 create (LayoutInflater inflater,ViewGroup parent) { return MyHolder3(inflater.inflate(R.layout.xxxx,parent,false )); } private MyHolder3 (View itemView) { super (itemView); } @Override public void onBind (BaseBean bean) { } } interface OnHolderClickListener { void onHolderClick (BaseHolder holder,View view) ; }
以上,就是一个相对完整的demo了。而最后一个demo,基本上也算是实际项目中经常遇到的需求,通过这种模式,可以实现Header
,Footer
的效果,不要再去找什么支持addHeader
的RecyclerView
了,他们的实现方式其实和我上面写的是一样的,都是多布局实现的。 你可能会说:好多类啊,好复杂啊。但是你想过没有,我们这样写过之后,代码会更加专注。HolderA 里面只需要专注的处理他自己相关的逻辑,比如Bean 过来了,我怎么绑定,哪些需要点击事件,我有几张图片需要加载,里面进度条走到多少,文本怎么排版。甚至里面可以是一个Banner 图,比如这个项目 。 这样代码就很纯粹,业务代码的牵连就很少,代码量并没有减少什么,但是整体逻辑的梳理和代码的整洁,就会好很多了。 以上,就是我对于RecyclerView
的一些简单理解了,希望可以帮到你。