来自https://developer.android.com/preview/material/ui-widgets.html
当我们创建时,RecyclerView.Adapter
我们必须指定ViewHolder
将与适配器绑定.
public class MyAdapter extends RecyclerView.Adapter{ private String[] mDataset; public MyAdapter(String[] myDataset) { mDataset = myDataset; } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(TextView v) { super(v); mTextView = v; } } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false); //findViewById... ViewHolder vh = new ViewHolder(v); return vh; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.mTextView.setText(mDataset[position]); } @Override public int getItemCount() { return mDataset.length; } }
那么,是否可以RecyclerView
使用多种视图类型进行创建?
是的,这是可能的.只需实现getItemViewType(),并处理中的viewType
参数onCreateViewHolder()
.
所以你做的事情如下:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { class ViewHolder0 extends RecyclerView.ViewHolder { ... public ViewHolder0(View itemView){ ... } } class ViewHolder2 extends RecyclerView.ViewHolder { ... public ViewHolder2(View itemView){ ... } @Override public int getItemViewType(int position) { // Just as an example, return 0 or 2 depending on position // Note that unlike in ListView adapters, types don't have to be contiguous return position % 2 * 2; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case 0: return new ViewHolder0(...); case 2: return new ViewHolder2(...); ... } } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { switch (holder.getItemViewType()) { case 0: ViewHolder0 viewHolder0 = (ViewHolder0)holder; ... break; case 2: ViewHolder2 viewHolder2 = (ViewHolder2)holder; ... break; } } }
如果视图类型的布局只有几个,绑定逻辑很简单,请遵循Anton的解决方案.
但是,如果您需要管理复杂的布局和绑定逻辑,代码将会很混乱.
我相信以下解决方案对需要处理复杂视图类型的人有用.
基础DataBinder类
abstract public class DataBinder<T extends RecyclerView.ViewHolder> { private DataBindAdapter mDataBindAdapter; public DataBinder(DataBindAdapter dataBindAdapter) { mDataBindAdapter = dataBindAdapter; } abstract public T newViewHolder(ViewGroup parent); abstract public void bindViewHolder(T holder, int position); abstract public int getItemCount(); ...... }
在创建单一视图类型时,此类中定义的函数与适配器类几乎完全相同.
对于每种视图类型,通过扩展此DataBinder来创建类.
示例DataBinder类
public class Sample1Binder extends DataBinder<Sample1Binder.ViewHolder> { private List<String> mDataSet = new ArrayList(); public Sample1Binder(DataBindAdapter dataBindAdapter) { super(dataBindAdapter); } @Override public ViewHolder newViewHolder(ViewGroup parent) { View view = LayoutInflater.from(parent.getContext()).inflate( R.layout.layout_sample1, parent, false); return new ViewHolder(view); } @Override public void bindViewHolder(ViewHolder holder, int position) { String title = mDataSet.get(position); holder.mTitleText.setText(title); } @Override public int getItemCount() { return mDataSet.size(); } public void setDataSet(List<String> dataSet) { mDataSet.addAll(dataSet); } static class ViewHolder extends RecyclerView.ViewHolder { TextView mTitleText; public ViewHolder(View view) { super(view); mTitleText = (TextView) view.findViewById(R.id.title_type1); } } }
要管理DataBinder类,请创建适配器类.
Base DataBindAdapter类
abstract public class DataBindAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return getDataBinder(viewType).newViewHolder(parent); } @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { int binderPosition = getBinderPosition(position); getDataBinder(viewHolder.getItemViewType()).bindViewHolder(viewHolder, binderPosition); } @Override public abstract int getItemCount(); @Override public abstract int getItemViewType(int position); public abstract <T extends DataBinder> T getDataBinder(int viewType); public abstract int getPosition(DataBinder binder, int binderPosition); public abstract int getBinderPosition(int position); ...... }
通过扩展此基类来创建类,然后实例化DataBinder类并覆盖抽象方法
getItemCount
返回DataBinders的项目总数
getItemViewType
定义适配器位置和视图类型之间的映射逻辑.
getDataBinder
根据视图类型返回DataBinder实例
getPosition
将转换逻辑定义为指定DataBinder中位置的适配器位置
getBinderPosition
从适配器位置将转换逻辑定义到DataBinder中的位置
希望这个解决方案会有所帮助.
我在GitHub中留下了更多细节解决方案和样本,如果需要,请参考以下链接.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter
下面不是伪代码,我测试了它,它对我有用.
我想在我的recyclerview中创建一个headerview,然后在标题下方显示一个用户可以单击的图片列表.
我在代码中使用了一些开关,不知道这是否是最有效的方法,所以请随意发表您的意见:
public class ViewHolder extends RecyclerView.ViewHolder{ //These are the general elements in the RecyclerView public TextView place; public ImageView pics; //This is the Header on the Recycler (viewType = 0) public TextView name, description; //This constructor would switch what to findViewBy according to the type of viewType public ViewHolder(View v, int viewType) { super(v); if (viewType == 0) { name = (TextView) v.findViewById(R.id.name); decsription = (TextView) v.findViewById(R.id.description); } else if (viewType == 1) { place = (TextView) v.findViewById(R.id.place); pics = (ImageView) v.findViewById(R.id.pics); } } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v; ViewHolder vh; // create a new view switch (viewType) { case 0: //This would be the header view in my Recycler v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.recyclerview_welcome, parent, false); vh = new ViewHolder(v,viewType); return vh; default: //This would be the normal list with the pictures of the places in the world v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.recyclerview_picture, parent, false); vh = new ViewHolder(v, viewType); v.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Intent intent = new Intent(mContext, nextActivity.class); intent.putExtra("ListNo",mRecyclerView.getChildPosition(v)); mContext.startActivity(intent); } }); return vh; } } //Overriden so that I can display custom rows in the recyclerview @Override public int getItemViewType(int position) { int viewType = 1; //Default is 1 if (position == 0) viewType = 0; //if zero, it will be a header view return viewType; } @Override public void onBindViewHolder(ViewHolder holder, int position) { //position == 0 means its the info header view on the Recycler if (position == 0) { holder.name.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext,"name clicked", Toast.LENGTH_SHORT).show(); } }); holder.description.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext,"description clicked", Toast.LENGTH_SHORT).show(); } }); //this means it is beyond the headerview now as it is no longer 0. For testing purposes, I'm alternating between two pics for now } else if (position > 0) { holder.place.setText(mDataset[position]); if (position % 2 == 0) { holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic1)); } if (position % 2 == 1) { holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic2)); } } }
RecyclerView可以拥有您想要的任意数量的视图,但为了更好的可读性,我们可以看看如何使用两个ViewHolders创建一个.
它可以通过三个简单的步骤完成
覆盖 public int getItemViewType(int position)
根据onCreateViewHolder()
方法中的ViewType返回不同的ViewHolders
基于onBindViewHolder()
方法中的itemViewType填充视图
这是一个小代码片段
public class YourListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int LAYOUT_ONE= 0; private static final int LAYOUT_TWO= 1; @Override public int getItemViewType(int position) { if(position==0) return LAYOUT_ONE; else return LAYOUT_TWO; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view =null; RecyclerView.ViewHolder viewHolder = null; if(viewType==LAYOUT_ONE) { view = LayoutInflater.from(parent.getContext()).inflate(R.layout.one,parent,false); viewHolder = new ViewHolderOne(view); } else { view = LayoutInflater.from(parent.getContext()).inflate(R.layout.two,parent,false); viewHolder= new ViewHolderTwo(view); } return viewHolder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { if(holder.getItemViewType()== LAYOUT_ONE) { // Typecast Viewholder // Set Viewholder properties // Add any click listener if any } else { ViewHolderOne vaultItemHolder = (ViewHolderOne) holder; vaultItemHolder.name.setText(displayText); vaultItemHolder.name.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ....... } }); } } //**************** VIEW HOLDER 1 ******************// public class ViewHolderOne extends RecyclerView.ViewHolder { public TextView name; public ViewHolderOne(View itemView) { super(itemView); name = (TextView)itemView.findViewById(R.id.displayName); } } //**************** VIEW HOLDER 2 ******************// public class ViewHolderTwo extends RecyclerView.ViewHolder{ public ViewHolderTwo(View itemView) { super(itemView); ..... Do something } } }
在我看来,创建这种recyclerView的起点是这种方法的知识.因为这个方法是可选的覆盖因此它默认在RecylerView类中不可见,这反过来又使许多开发人员(包括我)想知道从哪里开始.一旦你知道这个方法存在,创建这样的RecyclerView将是一个很小的步骤.
让我们看一个例子来证明我的观点.如果要在备用位置显示两个布局,请执行此操作
@Override public int getItemViewType(int position) { if(position%2==0) // Even position return LAYOUT_ONE; else // Odd position return LAYOUT_TWO; }
查看我已实现此项目的项目
对的,这是可能的.
写一个通用视图持有者:
public abstract class GenericViewHolder extends RecyclerView.ViewHolder { public GenericViewHolder(View itemView) { super(itemView); } public abstract void setDataOnView(int position); }
然后创建视图持有者并使它们扩展GenericViewHolder.例如,这一个:
public class SectionViewHolder extends GenericViewHolder{ public final View mView; public final TextView dividerTxtV; public SectionViewHolder(View itemView) { super(itemView); mView = itemView; dividerTxtV = (TextView) mView.findViewById(R.id.dividerTxtV); } @Override public void setDataOnView(int position) { try { String title= sections.get(position); if(title!= null) this.dividerTxtV.setText(title); }catch (Exception e){ new CustomError("Error!"+e.getMessage(), null, false, null, e); } } }
那么RecyclerView.Adapter类将如下所示:
public class MyClassRecyclerViewAdapter extends RecyclerView.Adapter<MyClassRecyclerViewAdapter.GenericViewHolder> { @Override public int getItemViewType(int position) { // depends on your problem switch (position) { case : return VIEW_TYPE1; case : return VIEW_TYPE2; ... } } @Override public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; if(viewType == VIEW_TYPE1){ view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout1, parent, false); return new SectionViewHolder(view); }else if( viewType == VIEW_TYPE2){ view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout2, parent, false); return new OtherViewHolder(view); } // Cont. other view holders ... return null; } @Override public void onBindViewHolder(GenericViewHolder holder, int position) { holder.setDataOnView(position); }
按照Anton的解决方案,拿出这个ViewHolder
来保存/处理/委托不同类型的布局.但是当回收视图ViewHolder
不是数据卷的类型时,不确定更换新布局是否有效.
所以基本上
onCreateViewHolder(ViewGroup parent, int viewType)
只在需要新视图布局时调用;
getItemViewType(int position)
将被要求viewType
;
onBindViewHolder(ViewHolder holder, int position)
在回收视图时总是调用(引入新数据并尝试显示该数据ViewHolder
).
因此,当onBindViewHolder
调用它时,需要放入正确的视图布局并更新ViewHolder
.
替换视图布局的方法是否正确ViewHolder
,或者出现问题?感谢任何评论!
public int getItemViewType(int position) { TypedData data = mDataSource.get(position); return data.type; } public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return ViewHolder.makeViewHolder(parent, viewType); } public void onBindViewHolder(ViewHolder holder, int position) { TypedData data = mDataSource.get(position); holder.updateData(data); } /// public static class ViewHolder extends RecyclerView.ViewHolder { ViewGroup mParentViewGroup; View mCurrentViewThisViewHolderIsFor; int mDataType; public TypeOneViewHolder mTypeOneViewHolder; public TypeTwoViewHolder mTypeTwoViewHolder; static ViewHolder makeViewHolder(ViewGroup vwGrp, int dataType) { View v = getLayoutView(vwGrp, dataType); return new ViewHolder(vwGrp, v, viewType); } static View getLayoutView(ViewGroup vwGrp, int dataType) { int layoutId = getLayoutId(dataType); return LayoutInflater.from(vwGrp.getContext()) .inflate(layoutId, null); } static int getLayoutId(int dataType) { if (dataType == TYPE_ONE) { return R.layout.type_one_layout; } else if (dataType == TYPE_TWO) { return R.layout.type_two_layout; } } public ViewHolder(ViewGroup vwGrp, View v, int dataType) { super(v); mDataType = dataType; mParentViewGroup = vwGrp; mCurrentViewThisViewHolderIsFor = v; if (data.type == TYPE_ONE) { mTypeOneViewHolder = new TypeOneViewHolder(v); } else if (data.type == TYPE_TWO) { mTypeTwoViewHolder = new TypeTwoViewHolder(v); } } public void updateData(TypeData data) { mDataType = data.type; if (data.type == TYPE_ONE) { mTypeTwoViewHolder = null; if (mTypeOneViewHolder == null) { View newView = getLayoutView(mParentViewGroup, data.type); /** * how to replace new view with the view in the parent view container ??? */ replaceView(mCurrentViewThisViewHolderIsFor, newView); mCurrentViewThisViewHolderIsFor = newView; mTypeOneViewHolder = new TypeOneViewHolder(newView); } mTypeOneViewHolder.updateDataTypeOne(data); } else if (data.type == TYPE_TWO){ mTypeOneViewHolder = null; if (mTypeTwoViewHolder == null) { View newView = getLayoutView(mParentViewGroup, data.type); /** * how to replace new view with the view in the parent view container ??? */ replaceView(mCurrentViewThisViewHolderIsFor, newView); mCurrentViewThisViewHolderIsFor = newView; mTypeTwoViewHolder = new TypeTwoViewHolder(newView); } mTypeTwoViewHolder.updateDataTypeOne(data); } } } public static void replaceView(View currentView, View newView) { ViewGroup parent = (ViewGroup)currentView.getParent(); if(parent == null) { return; } final int index = parent.indexOfChild(currentView); parent.removeView(currentView); parent.addView(newView, index); }
编辑: ViewHolder具有成员mItemViewType来保存视图
编辑:貌似在onBindViewHolder(ViewHolder holder,int position)中传入的ViewHolder已经通过查看getItemViewType(int position)来获取(或创建)以确保它是匹配的,所以可能不必担心ViewHolder的type与数据[position]的类型不匹配.有谁知道onBindViewHolder()中的ViewHolder是如何被拾取的?
编辑:看起来回收ViewHolder
是按类型选择的,因此没有战士.
编辑:http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/回答这个问题.
它得到的回收ViewHolder
如下:
holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));
如果没有找到ViewHolder
正确类型的回收,则创建一个新的.
public ViewHolder getRecycledView(int viewType) { final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType); if (scrapHeap != null && !scrapHeap.isEmpty()) { final int index = scrapHeap.size() - 1; final ViewHolder scrap = scrapHeap.get(index); scrapHeap.remove(index); return scrap; } return null; } View getViewForPosition(int position, boolean dryRun) { ...... if (holder == null) { final int offsetPosition = mAdapterHelper.findPositionOffset(position); if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item " + "position " + position + "(offset:" + offsetPosition + ")." + "state:" + mState.getItemCount()); } final int type = mAdapter.getItemViewType(offsetPosition); // 2) Find from scrap via stable ids, if exists if (mAdapter.hasStableIds()) { holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun); if (holder != null) { // update position holder.mPosition = offsetPosition; fromScrap = true; } } if (holder == null && mViewCacheExtension != null) { // We are NOT sending the offsetPosition because LayoutManager does not // know it. final View view = mViewCacheExtension .getViewForPositionAndType(this, position, type); if (view != null) { holder = getChildViewHolder(view); if (holder == null) { throw new IllegalArgumentException("getViewForPositionAndType returned" + " a view which does not have a ViewHolder"); } else if (holder.shouldIgnore()) { throw new IllegalArgumentException("getViewForPositionAndType returned" + " a view that is ignored. You must call stopIgnoring before" + " returning this view."); } } } if (holder == null) { // fallback to recycler // try recycler. // Head to the shared pool. if (DEBUG) { Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared " + "pool"); } holder = getRecycledViewPool() .getRecycledView(mAdapter.getItemViewType(offsetPosition)); if (holder != null) { holder.resetInternal(); if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } } if (holder == null) { holder = mAdapter.createViewHolder(RecyclerView.this, mAdapter.getItemViewType(offsetPosition)); if (DEBUG) { Log.d(TAG, "getViewForPosition created new ViewHolder"); } } } boolean bound = false; if (mState.isPreLayout() && holder.isBound()) { // do not update unless we absolutely have to. holder.mPreLayoutPosition = position; } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { if (DEBUG && holder.isRemoved()) { throw new IllegalStateException("Removed holder should be bound and it should" + " come here only in pre-layout. Holder: " + holder); } final int offsetPosition = mAdapterHelper.findPositionOffset(position); mAdapter.bindViewHolder(holder, offsetPosition); attachAccessibilityDelegate(holder.itemView); bound = true; if (mState.isPreLayout()) { holder.mPreLayoutPosition = position; } } final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); final LayoutParams rvLayoutParams; if (lp == null) { rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); holder.itemView.setLayoutParams(rvLayoutParams); } else if (!checkLayoutParams(lp)) { rvLayoutParams = (LayoutParams) generateLayoutParams(lp); holder.itemView.setLayoutParams(rvLayoutParams); } else { rvLayoutParams = (LayoutParams) lp; } rvLayoutParams.mViewHolder = holder; rvLayoutParams.mPendingInvalidate = fromScrap && bound; return holder.itemView; }
对的,这是可能的.在您的适配器getItemViewType布局像这样....
public class MultiViewTypeAdapter extends RecyclerView.Adapter { private ArrayList<Model>dataSet; Context mContext; int total_types; MediaPlayer mPlayer; private boolean fabStateVolume = false; public static class TextTypeViewHolder extends RecyclerView.ViewHolder { TextView txtType; CardView cardView; public TextTypeViewHolder(View itemView) { super(itemView); this.txtType = (TextView) itemView.findViewById(R.id.type); this.cardView = (CardView) itemView.findViewById(R.id.card_view); } } public static class ImageTypeViewHolder extends RecyclerView.ViewHolder { TextView txtType; ImageView image; public ImageTypeViewHolder(View itemView) { super(itemView); this.txtType = (TextView) itemView.findViewById(R.id.type); this.image = (ImageView) itemView.findViewById(R.id.background); } } public static class AudioTypeViewHolder extends RecyclerView.ViewHolder { TextView txtType; FloatingActionButton fab; public AudioTypeViewHolder(View itemView) { super(itemView); this.txtType = (TextView) itemView.findViewById(R.id.type); this.fab = (FloatingActionButton) itemView.findViewById(R.id.fab); } } public MultiViewTypeAdapter(ArrayList<Model>data, Context context) { this.dataSet = data; this.mContext = context; total_types = dataSet.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { case Model.TEXT_TYPE: view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_type, parent, false); return new TextTypeViewHolder(view); case Model.IMAGE_TYPE: view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_type, parent, false); return new ImageTypeViewHolder(view); case Model.AUDIO_TYPE: view = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_type, parent, false); return new AudioTypeViewHolder(view); } return null; } @Override public int getItemViewType(int position) { switch (dataSet.get(position).type) { case 0: return Model.TEXT_TYPE; case 1: return Model.IMAGE_TYPE; case 2: return Model.AUDIO_TYPE; default: return -1; } } @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) { Model object = dataSet.get(listPosition); if (object != null) { switch (object.type) { case Model.TEXT_TYPE: ((TextTypeViewHolder) holder).txtType.setText(object.text); break; case Model.IMAGE_TYPE: ((ImageTypeViewHolder) holder).txtType.setText(object.text); ((ImageTypeViewHolder) holder).image.setImageResource(object.data); break; case Model.AUDIO_TYPE: ((AudioTypeViewHolder) holder).txtType.setText(object.text); } } } @Override public int getItemCount() { return dataSet.size(); } }
供参考链接:https://www.journaldev.com/12372/android-recyclerview-example
我有一个更好的解决方案,允许以声明和类型安全的方式创建多个视图类型.它是用Kotlin写的,顺便说一句,这真的很棒.
所有必需视图类型的简单视图持有者
class ViewHolderMedium(itemView: View) : RecyclerView.ViewHolder(itemView) {
val icon: ImageView = itemView.findViewById(R.id.icon) as ImageView
val label: TextView = itemView.findViewById(R.id.label) as TextView
}
有一个适配器数据项的抽象.请注意,视图类型由特定视图持有者类的hashCode表示(Kotlin中的KClass)
trait AdapterItem {
val viewType: Int
fun bindViewHolder(viewHolder: RecyclerView.ViewHolder)
}
abstract class AdapterItemBase<T>(val viewHolderClass: KClass<T>) : AdapterItem {
override val viewType: Int = viewHolderClass.hashCode()
abstract fun bindViewHolder(viewHolder: T)
override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
bindViewHolder(viewHolder as T)
}
}
只bindViewHolder
需要在具体的适配器项类中重写(类型安全方式)
class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase<ViewHolderMedium>(ViewHolderMedium::class) {
override fun bindViewHolder(viewHolder: ViewHolderMedium) {
viewHolder.icon.setImageDrawable(icon)
viewHolder.label.setText(label)
viewHolder.itemView.setOnClickListener { onClick() }
}
}
这些AdapterItemMedium
对象的列表是实际接受的适配器的数据源,List<AdapterItem>
见下文.
此解决方案的重要部分是视图持有者工厂,它将提供特定ViewHolder的新实例
class ViewHolderProvider {
private val viewHolderFactories = hashMapOf<Int, Pair<Int, Any>>()
fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val (layoutId: Int, f: Any) = viewHolderFactories.get(viewType)
val viewHolderFactory = f as (View) -> RecyclerView.ViewHolder
val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
return viewHolderFactory(view)
}
fun registerViewHolderFactory<T>(key: KClass<T>, layoutId: Int, viewHolderFactory: (View) -> T) {
viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory))
}
}
简单的适配器类看起来像这样
public class MultitypeAdapter(val items: List<AdapterItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val viewHolderProvider = ViewHolderProvider() // inject ex Dagger2
init {
viewHolderProvider!!.registerViewHolderFactory(ViewHolderMedium::class, R.layout.item_medium, { itemView ->
ViewHolderMedium(itemView)
})
}
override fun getItemViewType(position: Int): Int {
return items[position].viewType
}
override fun getItemCount(): Int {
return items.size()
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
return viewHolderProvider!!.provideViewHolder(viewGroup, viewType)
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
items[position].bindViewHolder(viewHolder)
}
}
创建新视图类型只需3个步骤:
创建一个视图持有者类
创建一个适配器项类(从AdapterItemBase扩展)
注册视图持有者类 ViewHolderProvider
下面是这个概念的一个例子:android-drawer-template 它更进一步 - 视图类型充当微调器组件,可选适配器项.
这非常简单直接。
只需在适配器中重写getItemViewType()方法即可。根据数据返回不同的itemViewType值。例如,考虑具有成员isMale的Person类型的对象,如果isMale为true,则返回1,isMale为false,在getItemViewType()方法中返回2 。
现在谈到createViewHolder(ViewGroup父类,int viewType),基于不同的viewType yon可以膨胀不同的布局文件。像下面
if (viewType ==1){ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.male,parent,false); return new AdapterMaleViewHolder(view); } else{ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.female,parent,false); return new AdapterFemaleViewHolder(view); }
在onBindViewHolder(VH保持器,INT位置)检查其中支架是实例AdapterFemaleViewHolder
或AdapterMaleViewHolder
通过instanceof
,并相应地分配的值。
ViewHolder可能会像这样
class AdapterMaleViewHolder extends RecyclerView.ViewHolder { ... public AdapterMaleViewHolder(View itemView){ ... } } class AdapterFemaleViewHolder extends RecyclerView.ViewHolder { ... public AdapterFemaleViewHolder(View itemView){ ... } }