Android RecyclerView多类型布局卡片解决方案
程序员文章站
2022-11-14 10:35:26
背景
随着公司业务越来越复杂,在同一个列表中需要展示各种类型的数据。
总体结构
itemviewadapter: 每种类型的卡片分别都是不同的it...
背景
随着公司业务越来越复杂,在同一个列表中需要展示各种类型的数据。
总体结构
- itemviewadapter: 每种类型的卡片分别都是不同的itemviewadapter
- itemviewadapterfactory: 使用itemviewadapterfactory根据不同数据对应不同的itemviewadapter
- multirecyclerviewadapter: multirecyclerviewadapter就是recylerview.adapter,并是个itemviewadapterfactory。
- 具体只要继承multirecyclerviewadapter即可,实现itemviewadapterfactory中getviewtype、oncreateitemviewadapter两个方法
- contextmap: 整个adapter共用一个contextmap数据上下文,用于外部(例fragment等)与itemadapter交互、itemadapter之间交互等一系列数据传递,可以解决参数层层传递的问题
- recyclerviewholder: 通用recyclerview.viewholder,封装根据id获取view方法getview(viewid)、获取数据上下文方法getcontextmap()
使用方法
每种类型卡片item都实现itemviewadapter
package com.lkh.multiadapter; import android.support.annotation.layoutres; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import java.util.list; /** * 列表单项布局与数据绑定 * created by luokanghui on 2017/5/24. */ public abstract class itemviewadapter<e>{ /** * 返回列表单项view,如果view由资源layout加载而来,直接重写{@link #ongetlayoutid()}即可 * @param parent 父view,一般为recyclerview * @return 列表单项view */ public view oncreateview(viewgroup parent){ return layoutinflater.from(parent.getcontext()).inflate(ongetlayoutid() , parent, false); } /** * 当recyclerviewholder创建成功后调用,只会调用一次 * @param viewholder 单项view集合 */ public void oncreate(recyclerviewholder viewholder){ } /** * 返回单项布局的资源id,如果重写了{@link #oncreateview(viewgroup)},则此方法可能失效 * @return 单项布局layout id */ @layoutres protected abstract int ongetlayoutid(); /** * 把数据与view进行绑定,滑动时都会调用 * @param viewholder 单项view集合 * @param data 具体数据 * @param position 在列表中的位置 */ public abstract void binddata(recyclerviewholder viewholder, e data, int position); /** * 局部更新时调用 * @param viewholder 单项view集合 * @param data 具体数据 * @param position 在列表中的位置 * @param payloads 局部更新标志,不会为空(isempty()==false) */ public void binddata(recyclerviewholder viewholder, e data, int position, list<object> payloads){ } }
卡片1:
package com.lkh.multiadapter.sample; import android.widget.textview; import com.lkh.multiadapter.itemviewadapter; import com.lkh.multiadapter.r; import com.lkh.multiadapter.recyclerviewholder; /** * 卡片1实现 * created by luokanghui on 2019/3/18 */ public class sampleoneitemviewadapter extends itemviewadapter<dataone> { @override protected int ongetlayoutid() { //布局layout资源id return r.layout.item_one; } @override public void binddata(recyclerviewholder viewholder, dataone data, int position) { //根据id获取view textview tvcontent = viewholder.getview(r.id.tv_content); //数据绑定 tvcontent.settext(data.getcontent()); } }
package com.lkh.multiadapter.sample; /** * 卡片1数据 * created by luokanghui on 2019/3/18 */ public class dataone { private string content; public string getcontent() { return content; } public void setcontent(string content) { this.content = content; } }
item_one.xml:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#eeeeee" android:orientation="vertical"> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="卡片1" android:textcolor="#000000" /> <textview android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="50dp" android:gravity="center" android:textcolor="#000000" /> </linearlayout>
卡片2
package com.lkh.multiadapter.sample; import android.widget.textview; import com.lkh.multiadapter.itemviewadapter; import com.lkh.multiadapter.r; import com.lkh.multiadapter.recyclerviewholder; /** * 卡片2实现 * created by luokanghui on 2019/3/18 */ public class sampletwoitemviewadapter extends itemviewadapter<datatwo> { @override protected int ongetlayoutid() { //布局layout资源id return r.layout.item_two; } @override public void binddata(recyclerviewholder viewholder, datatwo data, int position) { //根据id获取view textview tvnum = viewholder.getview(r.id.tv_num); //数据绑定 tvnum.settext("num="+data.getnum()); } }
package com.lkh.multiadapter.sample; /** * 卡片2数据 * created by luokanghui on 2019/3/18 */ public class datatwo { private int num; public int getnum() { return num; } public void setnum(int num) { this.num = num; } }
item_two.xml:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#999999" android:orientation="vertical"> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="卡片2" android:textcolor="#0000ff" /> <textview android:id="@+id/tv_num" android:layout_width="wrap_content" android:layout_height="100dp" android:gravity="center" android:textcolor="#0000ff" /> </linearlayout>
总adapter,继承multirecyclerviewadapter
package com.lkh.multiadapter.sample; import com.lkh.multiadapter.itemviewadapter; import com.lkh.multiadapter.multirecyclerviewadapter; /** * 多布局adapter,根据不同data及position,使用不同itemviewadapter卡片 * created by luokanghui on 2019/3/18 */ public class samplemultiadapter extends multirecyclerviewadapter<object> { private static final int type_empty = 0;//空item private static final int type_one = 1;//卡片1 private static final int type_two = 2;//卡片2 @override public int getviewtype(object data, int position) { if (data instanceof dataone){//卡片1 return type_one; } if (data instanceof datatwo){//卡片2 return type_two; } return type_empty;//空item } @override public itemviewadapter oncreateitemviewadapter(int viewtype) { switch (viewtype){ case type_one://卡片1 return new sampleoneitemviewadapter(); case type_two://卡片2 return new sampletwoitemviewadapter(); default://空item return new emptyitemviewadapter(); } } }
recyclerview中使用
package com.lkh.multiadapter; import android.os.bundle; import android.support.v7.app.appcompatactivity; import android.support.v7.widget.linearlayoutmanager; import android.support.v7.widget.recyclerview; import com.lkh.multiadapter.sample.dataone; import com.lkh.multiadapter.sample.datatwo; import com.lkh.multiadapter.sample.samplemultiadapter; import java.util.arraylist; import java.util.list; public class mainactivity extends appcompatactivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); recyclerview recyclerview = findviewbyid(r.id.recycler_view); recyclerview.setlayoutmanager(new linearlayoutmanager(this)); samplemultiadapter adapter = new samplemultiadapter(); //设置数据 adapter.setdata(generatedata()); //设置adapter recyclerview.setadapter(adapter); } //造测试数据 private list<object> generatedata(){ list<object> list = new arraylist<>(); for (int i=0; i<20; i++){ dataone dataone = new dataone(); dataone.setcontent("这是卡片1数据:"+i); list.add(dataone); datatwo datatwo = new datatwo(); datatwo.setnum(i); list.add(datatwo); } return list; } }
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.recyclerview android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </linearlayout>
运行效果如下:
总的来说,实现一个多类型布局列表只需要写多个不同卡片itemviewadapter、继承multirecyclerviewadapter用来控制不同数据使用不同itemviewadapter,新增一个卡片只需要新增一个itemviewadapter,在multirecyclerviewadapter新加一项即可,不会影响其它卡片使用,而且itemviewadapter完全独立,可以很好的复用。
核心代码
package com.lkh.multiadapter; import android.support.v7.widget.recyclerview; import android.view.viewgroup; import java.util.list; /** * 多种布局adapter * created by luokanghui on 2017/5/24. */ public abstract class multirecyclerviewadapter<e> extends recyclerview.adapter<recyclerviewholder> implements itemviewadapterfactory<e> { public static final int no_type = -1; private list<e> datalist; protected final mapdata mmapdata = new mapdata(); public multirecyclerviewadapter setdata(list<e> list) { this.datalist = list; return this; } @override public recyclerviewholder oncreateviewholder(viewgroup parent, int viewtype) { itemviewadapter itemviewmodule = oncreateitemviewadapter(viewtype); recyclerviewholder recyclerviewholder = new recyclerviewholder(itemviewmodule.oncreateview(parent), itemviewmodule, this, getcontextmap()); itemviewmodule.oncreate(recyclerviewholder); return recyclerviewholder; } @override public void onbindviewholder(recyclerviewholder holder, int position) { if (checkitems(position)) { return; } holder.itemviewadapter.binddata(holder, datalist.get(position), position); } @override public void onbindviewholder(recyclerviewholder holder, int position, list<object> payloads) { if (checkitems(position)) { return; } if (payloads.isempty()) { super.onbindviewholder(holder, position, payloads); } else { holder.itemviewadapter.binddata(holder, datalist.get(position), position, payloads); } } @override public int getitemviewtype(int position) { if (checkitems(position)) { return no_type; } return getviewtype(datalist.get(position), position); } @override public int getitemcount() { return datalist == null ? 0 : datalist.size(); } /** * true表示没通过 */ private boolean checkitems(int position) { return datalist == null || position < 0 || position >= datalist.size(); } @override public mapdata getcontextmap() { return mmapdata; } }
package com.lkh.multiadapter; /** * 多布局itemviewadapter创建者 * created by luokanghui on 2017/5/24. */ public interface itemviewadapterfactory<e> { /** * 返回itemviewadapter的类型 * 建议根据data的数据类型判断不同的viewtype * @param data 具体数据 * @param position 在列表中的位置 * @return 类型 */ int getviewtype(e data, int position); /** * 根据不同的viewtype返回不同的itemviewadapter * @param viewtype 类型 * @return itemviewadapter */ itemviewadapter<? extends e> oncreateitemviewadapter(int viewtype); /** * 上下文数据 * @return */ mapdata getcontextmap(); }
package com.lkh.multiadapter; import android.support.v7.widget.recyclerview; import android.util.sparsearray; import android.view.view; /** * viewholder基类 */ public final class recyclerviewholder extends recyclerview.viewholder { private final sparsearray<view> views; itemviewadapter itemviewadapter; private final recyclerview.adapter adapter; private final mapdata mmapdata ; public recyclerviewholder(view itemview, itemviewadapter itemviewadapter, recyclerview.adapter adapter, mapdata mapdata) { super(itemview); this.views = new sparsearray<>(); this.itemviewadapter = itemviewadapter; this.adapter = adapter; this.mmapdata = mapdata; } /** * 根据id获取view,如果缓存中存在,直接使用缓存中的,避免重复执行findviewbyid */ @suppresswarnings("unchecked") public <t extends view> t getview(int viewid) { view view = views.get(viewid); if (view == null) { view = itemview.findviewbyid(viewid); views.put(viewid, view); } return (t) view; } public recyclerview.adapter getadapter(){ return adapter; } /** * 获取数据上下文 */ public mapdata getcontextmap(){ return mmapdata; } }
package com.lkh.multiadapter; import android.support.annotation.layoutres; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import java.util.list; /** * 列表单项布局与数据绑定 * created by luokanghui on 2017/5/24. */ public abstract class itemviewadapter<e>{ /** * 返回列表单项view,如果view由资源layout加载而来,直接重写{@link #ongetlayoutid()}即可 * @param parent 父view,一般为recyclerview * @return 列表单项view */ public view oncreateview(viewgroup parent){ return layoutinflater.from(parent.getcontext()).inflate(ongetlayoutid() , parent, false); } /** * 当recyclerviewholder创建成功后调用,只会调用一次 * @param viewholder 单项view集合 */ public void oncreate(recyclerviewholder viewholder){ } /** * 返回单项布局的资源id,如果重写了{@link #oncreateview(viewgroup)},则此方法可能失效 * @return 单项布局layout id */ @layoutres protected abstract int ongetlayoutid(); /** * 把数据与view进行绑定,滑动时都会调用 * @param viewholder 单项view集合 * @param data 具体数据 * @param position 在列表中的位置 */ public abstract void binddata(recyclerviewholder viewholder, e data, int position); /** * 局部更新时调用 * @param viewholder 单项view集合 * @param data 具体数据 * @param position 在列表中的位置 * @param payloads 局部更新标志,不会为空(isempty()==false) */ public void binddata(recyclerviewholder viewholder, e data, int position, list<object> payloads){ } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。