Как создать RecyclerView с несколькими типами просмотра?

Из 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 () и позаботьтесь о параметре onCreateViewHolder() в onCreateViewHolder() .

Итак, вы делаете что-то вроде:

 public class MyAdapter extends RecyclerView.Adapter { 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; } } } 

Если макеты для типов просмотра – всего лишь несколько, а обязательная логика проста, следуйте за решением Антона.
Но код будет беспорядочным, если вам нужно управлять сложными макетами и логикой привязки.

Я считаю, что следующее решение будет полезно для тех, кому необходимо обрабатывать сложные типы просмотров.

Базовый class DataBinder

 abstract public class DataBinder { 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(); ...... } 

Функции, необходимые для определения в этом classе, почти одинаковы с classом адаптера при создании единого типа представления.
Для каждого типа представления создайте class, расширив этот DataBinder.

Пример classа DataBinder

 public class Sample1Binder extends DataBinder { private List 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 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); } } } 

Чтобы управлять classами DataBinder, создайте class адаптера.

Базовый class DataBindAdapter

 abstract public class DataBindAdapter extends RecyclerView.Adapter { @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 getDataBinder(int viewType); public abstract int getPosition(DataBinder binder, int binderPosition); public abstract int getBinderPosition(int position); ...... } 

Создайте class, расширив этот базовый class, а затем создайте экземпляры classов DataBinder и переопределите абстрактные методы

  1. getItemCount
    Возвращает общее количество элементов DataBinders

  2. getItemViewType
    Определите логику отображения между положением адаптера и видом.

  3. getDataBinder
    Верните экземпляр DataBinder на основе типа представления

  4. GetPosition
    Определить логику преобразования в положение адаптера из позиции в указанном DataBinder

  5. getBinderPosition
    Определить логику преобразования в положение в DataBinder из положения адаптера

Надеюсь, что это решение будет полезно.
Я оставил более подробное решение и образцы в GitHub, поэтому, если вам нужно, обратитесь к следующей ссылке.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter

Ниже приведен псевдокод, и я тестировал его, и он работал для меня.

Я хотел создать headerview в моем recyclerview, а затем отобразить список изображений под заголовком, на который пользователь может щелкнуть.

Я использовал несколько переключателей в своем коде, не знаю, является ли это самым эффективным способом сделать это, поэтому не стесняйтесь давать свои комментарии:

  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)); } } } 

Да, это возможно.

Напишите общий держатель вида:

  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); } } } 

то class RecyclerView.Adapter будет выглядеть следующим образом:

 public class MyClassRecyclerViewAdapter extends RecyclerView.Adapter { @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); } 

после решения Антона, ViewHolder этот ViewHolder который удерживает / обрабатывает / делегирует разные типы макетов. Но не уверен, что замена нового макета будет работать, если ViewHolder в ViewHolder просмотра не является типом данных.

Таким образом, onCreateViewHolder(ViewGroup parent, int viewType) вызывается только тогда, когда требуется новый макет представления;

getItemViewType(int position) будет вызываться для viewType ;

onBindViewHolder(ViewHolder holder, int position) всегда вызывается при onBindViewHolder(ViewHolder holder, int position) новые данные и пытаются отобразить их с помощью ViewHolder ).

Поэтому, когда onBindViewHolder ему нужно вставить правильный макет представления и обновить ViewHolder .

Правильно ли 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, int position) переданный ViewHolder был поднят (или создан), просмотрев getItemViewType (int position), чтобы убедиться, что это совпадение, поэтому, возможно, не стоит беспокоиться о том, что ViewHolder’s тип не соответствует типу данных [позиция]. Кто-нибудь знает больше, как подобрал ViewHolder в onBindViewHolder ()?

Edit: Похоже, что 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 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 Layout, как это ….

  public class MultiViewTypeAdapter extends RecyclerView.Adapter { private ArrayListdataSet; 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(ArrayListdata, 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

У меня есть лучшее решение, которое позволяет создавать несколько типов просмотра декларативным и безопасным типом. Это написано в Котлине, который, кстати, хорош.

Простые держатели для всех типов просмотра

 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 } 

Существует абстракция элемента данных адаптера. Обратите внимание, что тип представления представлен hash-кодом определенного classа держателя вида (KClass в Котлине)

 trait AdapterItem { val viewType: Int fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) } abstract class AdapterItemBase(val viewHolderClass: KClass) : AdapterItem { override val viewType: Int = viewHolderClass.hashCode() abstract fun bindViewHolder(viewHolder: T) override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) { bindViewHolder(viewHolder as T) } } 

Только bindViewHolder необходимо переопределить в конкретных classах элементов адаптера (введите безопасный способ)

 class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase(ViewHolderMedium::class) { override fun bindViewHolder(viewHolder: ViewHolderMedium) { viewHolder.icon.setImageDrawable(icon) viewHolder.label.setText(label) viewHolder.itemView.setOnClickListener { onClick() } } } 

Список таких объектов AdapterItemMedium является источником данных для адаптера, который фактически принимает List показано ниже.

Важной частью этого решения является фабрика держателей представлений, которая предоставит свежие экземпляры конкретного ViewHolder

 class ViewHolderProvider { private val viewHolderFactories = hashMapOf>() 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(key: KClass, layoutId: Int, viewHolderFactory: (View) -> T) { viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory)) } } 

И простой class адаптера выглядит так:

 public class MultitypeAdapter(val items: List) : RecyclerView.Adapter() { 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 шага для создания нового типа вида:

  1. создать class держателя вида
  2. создать class элемента адаптера (от AdapterItemBase)
  3. class просмотра ViewHolderProvider в ViewHolderProvider

Вот пример этой концепции: шаблон-ящик для Android. Он идет еще дальше – тип вида, который действует как компонент прядильщика, выбираемые элементы адаптера.

Это очень просто и прямо.

Просто переопределите метод getItemViewType () в вашем адаптере. На основе данных возвращаются разные значения itemViewType. Например, рассмотрим объект типа Person с членом isMale, если isMale – true, return 1 и isMale – false, return 2 – в методе getItemViewType () .

Теперь вы попадаете в 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 position) проверьте, где владелец является экземпляром AdapterFemaleViewHolder или AdapterMaleViewHolder instanceof и соответственно присваивает значения.

ViewHolder Может быть так

  class AdapterMaleViewHolder extends RecyclerView.ViewHolder { ... public AdapterMaleViewHolder(View itemView){ ... } } class AdapterFemaleViewHolder extends RecyclerView.ViewHolder { ... public AdapterFemaleViewHolder(View itemView){ ... } } 

На самом деле, я хотел бы улучшить ответ Антона .

Поскольку getItemViewType(int position) возвращает целочисленное значение, вы можете вернуть идентификатор ресурса макета, который вам нужно раздуть. Таким образом, вы сохраните некоторую логику в onCreateViewHolder(ViewGroup parent, int viewType) .

Кроме того, я бы не предложил делать интенсивные вычисления в getItemCount() поскольку эта конкретная функция вызывается не менее 5 раз при рендеринге списка, а также при рендеринге каждого элемента за видимые элементы. К сожалению, поскольку notifyDatasetChanged() является окончательным, его нельзя переопределить, но вы можете вызвать его из другой функции внутри адаптера.

Вы можете использовать библиотеку: https://github.com/vivchar/RendererRecyclerViewAdapter

 mRecyclerViewAdapter = new RendererRecyclerViewAdapter(); /* included from library */ mRecyclerViewAdapter.registerRenderer(new SomeViewRenderer(SomeModel.TYPE, this)); mRecyclerViewAdapter.registerRenderer(...); /* you can use several types of cells */ 

`

Для каждого элемента вам необходимо реализовать ViewRenderer, ViewHolder, SomeModel:

ViewHolder – это простой просмотрщик ресайклеров.

SomeModel – это ваша модель с интерфейсом ItemModel

 public class SomeViewRenderer extends ViewRenderer { public SomeViewRenderer(final int type, final Context context) { super(type, context); } @Override public void bindView(@NonNull final SomeModel model, @NonNull final SomeViewHolder holder) { holder.mTitle.setText(model.getTitle()); } @NonNull @Override public SomeViewHolder createViewHolder(@Nullable final ViewGroup parent) { return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false)); } } 

For more details you can look documentations.

Create different ViewHolder for different layout

RecyclerView can have any number of viewholders you want but for better readability lets see how to create one with two ViewHolders.

It can be done in three simple steps

  1. Override public int getItemViewType(int position)
  2. Return different ViewHolders based on the ViewType in onCreateViewHolder() method
  3. Populate View based on the itemViewType in onBindViewHolder() method

Here is a small code snippet

 public class YourListAdapter extends RecyclerView.Adapter { 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 } } } 

getItemViewType(int position) is the key

In my opinion the starting point to create this kind of recyclerView is the knowledge of this method. Since this method optional to override hence it is not visible by default in RecylerView class which makes many developers(including me) wonder where to begin. Once you know this method exists creating such RecyclerView would be a cakewalk.

Lets see one example to prove my point. If you want to show two layout at alternate positions do this

 @Override public int getItemViewType(int position) { if(position%2==0) // Even position return LAYOUT_ONE; else // Odd position return LAYOUT_TWO; } 
  • process.WaitForExit () асинхронно
  • Безопасно ли создавать виджеты Swing / AWT NOT на тему Dispatch Event?
  • Как я могу получить высоту и ширину панели навигации Android программно?
  • Обновление пользовательского интерфейса Android с помощью streamов
  • Переопределить цвета контекстного меню в Android
  • Более эффективный способ обновления пользовательского интерфейса от службы, чем намерения?
  • Как настроить панель выполнения на Android
  • Один или несколько ресурсов имеют цель «голова», но не «головной» компонент был определен в представлении
  • Сериализовать компоненты JavaFX
  • Как стиль PopupMenu?
  • Как эффективно использовать cardlayout в java для переключения с панели с помощью кнопок внутри различных панельных конструкторов
  • Давайте будем гением компьютера.