Java 如何创建具有多种视图类型的 RecyclerView?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26245139/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create RecyclerView with multiple view type?
提问by Pongpat
From https://developer.android.com/preview/material/ui-widgets.html
来自https://developer.android.com/preview/material/ui-widgets.html
When we create RecyclerView.Adapter
we have to specify ViewHolder
that will bind with the adapter.
当我们创建时,RecyclerView.Adapter
我们必须指定ViewHolder
将与适配器绑定。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
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;
}
}
So, is it possible to create RecyclerView
with multiple view types?
那么,是否可以RecyclerView
使用多种视图类型进行创建?
采纳答案by Anton Savin
Yes, it's possible. Just implement getItemViewType(), and take care of the viewType
parameter in onCreateViewHolder()
.
是的,这是可能的。只实现getItemViewType() ,并采取照顾viewType
的参数onCreateViewHolder()
。
So you do something like:
所以你做这样的事情:
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;
}
}
}
回答by lannyf
following Anton's solution, come up with this ViewHolder
which holds/handles/delegates different type of layouts.
But not sure if the replacing new layout would work when the recycling view's ViewHolder
is not type of the data roll in.
按照安东的解决方案,提出这个ViewHolder
持有/处理/委托不同类型的布局。但不确定当回收视图ViewHolder
不是数据滚入的类型时替换新布局是否有效。
So basically,
onCreateViewHolder(ViewGroup parent, int viewType)
is only called when new view layout is needed;
所以基本上,
onCreateViewHolder(ViewGroup parent, int viewType)
只有在需要新的视图布局时才会调用;
getItemViewType(int position)
will be called for the viewType
;
getItemViewType(int position)
将被调用viewType
;
onBindViewHolder(ViewHolder holder, int position)
is always called when recycling the view (new data is brought in and try to display with that ViewHolder
).
onBindViewHolder(ViewHolder holder, int position)
总是在回收视图时调用(引入新数据并尝试使用该数据显示ViewHolder
)。
So when onBindViewHolder
is called it needs to put in the right view layout and update the ViewHolder
.
所以当onBindViewHolder
被调用时它需要放入正确的视图布局并更新ViewHolder
.
Is the way correct to replacing the view layout for that ViewHolder
to be brought in, or any problem?
Appreciate any comment!
替换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);
}
Edit:ViewHolder has member mItemViewType to hold the view
编辑:ViewHolder 有成员 mItemViewType 来保存视图
Edit:looks like in onBindViewHolder(ViewHolder holder, int position) the ViewHolder passed in has been picked up (or created) by looked at getItemViewType(int position) to make sure it is a match, so may not need to worry there that ViewHolder's type does not match the data[position]'s type. Does anyone knows more how the ViewHolder in the onBindViewHolder() is picked up?
编辑:看起来在 onBindViewHolder(ViewHolder holder, int position) 中,传入的 ViewHolder 已经通过查看 getItemViewType(int position) 被拾取(或创建)以确保它是匹配的,所以可能不需要担心 ViewHolder 的类型与数据[位置]的类型不匹配。有谁知道 onBindViewHolder() 中的 ViewHolder 是如何被拾取的?
Edit:Looks like The recycle ViewHolder
is picked by type, so no warrior there.
编辑:看起来回收ViewHolder
是按类型挑选的,所以那里没有战士。
Edit: http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/answers this question.
编辑:http: //wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/回答这个问题。
It gets the recycle ViewHolder
like:
它得到ViewHolder
像这样的回收:
holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));
or create new one if not find recycle ViewHolder
of right type.
如果没有找到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;
}
回答by yqritc
If the layouts for view types are only a few and binding logics are simple, follow Anton's solution.
But the code will be messy if you need to manage the complex layouts and binding logics.
如果视图类型的布局很少,绑定逻辑比较简单,就按照安东的方案。
但是如果你需要管理复杂的布局和绑定逻辑,代码就会很混乱。
I believe the following solution will be useful for someone who need to handle complex view types.
我相信以下解决方案对于需要处理复杂视图类型的人会很有用。
Base DataBinder class
基础 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();
......
}
The functions needed to define in this class are pretty much same as the adapter class when creating the single view type.
For each view type, create the class by extending this DataBinder.
创建单个视图类型时,需要在此类中定义的函数与适配器类几乎相同。
对于每种视图类型,通过扩展此 DataBinder 来创建类。
Sample DataBinder class
示例 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);
}
}
}
In order to manage DataBinder classes, create adapter class.
为了管理 DataBinder 类,创建适配器类。
Base DataBindAdapter class
基础 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);
......
}
Create the class by extending this base class, and then instantiate DataBinder classes and override abstract methods
通过扩展这个基类来创建类,然后实例化DataBinder类并覆盖抽象方法
getItemCount
Return the total item count of DataBindersgetItemViewType
Define the mapping logic between the adapter position and view type.getDataBinder
Return the DataBinder instance based on the view typegetPosition
Define convert logic to the adapter position from the position in the specified DataBindergetBinderPosition
Define convert logic to the position in the DataBinder from the adapter position
getItemCount
返回 DataBinders 的总项目数getItemViewType
定义适配器位置和视图类型之间的映射逻辑。getDataBinder
根据视图类型返回DataBinder实例getPosition
定义从指定DataBinder中的位置到adapter位置的转换逻辑getBinderPosition
定义从adapter位置到DataBinder中位置的转换逻辑
Hope this solution will be helpful.
I left more detail solution and samples in GitHub, so please refer the following link if you need.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter
希望这个解决方案会有所帮助。
我在 GitHub 中留下了更详细的解决方案和示例,因此如果您需要,请参阅以下链接。
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter
回答by Simon
The below is not pseudocode and I have tested it and it has worked for me.
以下不是伪代码,我已经对其进行了测试,并且对我有用。
I wanted to create a headerview in my recyclerview and then display a list of pictures below the header which the user can click on.
我想在我的回收站视图中创建一个标题视图,然后在用户可以单击的标题下方显示一个图片列表。
I used a few switches in my code, don't know if that is the most efficient way to do this so feel free to give your comments:
我在我的代码中使用了一些开关,不知道这是否是最有效的方法,所以请随时提出您的意见:
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));
}
}
}
回答by Michal Faber
I have a better solution which allows to create multiple view types in a declarative and type safe way. It's written in Kotlin which btw is really nice.
我有一个更好的解决方案,它允许以声明性和类型安全的方式创建多个视图类型。它是用 Kotlin 编写的,顺便说一句非常好。
Simple view holders for all required view types
适用于所有必需视图类型的简单视图持有者
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
}
There is an abstraction of adapter data item. Note that a view type is represented by a hashCode of particular view holder class (KClass in Kotlin)
有一个适配器数据项的抽象。请注意,视图类型由特定视图持有者类(Kotlin 中的 KClass)的 hashCode 表示
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)
}
}
Only bindViewHolder
needs to be overriden in concrete adapter item classes (type safe way)
只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() }
}
}
List of such AdapterItemMedium
objects is a data source for the adapter which actually accepts List<AdapterItem>
see below.
此类AdapterItemMedium
对象的列表是实际接受的适配器的数据源,List<AdapterItem>
请参见下文。
Important part of this solution is a view holder factory which will provide fresh instances of a specific ViewHolder
此解决方案的重要部分是视图持有者工厂,它将提供特定 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))
}
}
And the simple adapter class looks like this
简单的适配器类看起来像这样
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)
}
}
Only 3 steps to create a new view type:
只需 3 个步骤即可创建新的视图类型:
- create a view holder class
- create an adapter item class (extending from AdapterItemBase)
- register view holder class in
ViewHolderProvider
- 创建一个视图持有者类
- 创建一个适配器项类(从 AdapterItemBase 扩展)
- 注册视图持有者类
ViewHolderProvider
Here is an example of this concept : android-drawer-templateIt goes even further - view type which act as a spinner component, selectable adapter items.
这是这个概念的一个例子:android-drawer-template它更进一步 - 视图类型充当微调组件,可选择的适配器项目。
回答by Islam Assi
Yes, it is possible.
对的,这是可能的。
Write a generic view holder:
编写一个通用的视图持有者:
public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void setDataOnView(int position);
}
then create your view holders and make them extend the GenericViewHolder. For example, this one:
然后创建您的视图持有者并使它们扩展 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);
}
}
}
then the RecyclerView.Adapter class will look like this one:
那么 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);
}
回答by Dragas
Actually, I'd like to improve on Anton's answer.
实际上,我想改进安东的回答。
Since getItemViewType(int position)
returns an integer value, you can return the layout resource ID you'd need to inflate. That way you'd save some logic in onCreateViewHolder(ViewGroup parent, int viewType)
method.
由于getItemViewType(int position)
返回一个整数值,您可以返回需要扩充的布局资源 ID。这样你就可以在onCreateViewHolder(ViewGroup parent, int viewType)
方法中保存一些逻辑。
Also, I wouldn't suggest doing intensive calculations in getItemCount()
as that particular function is called at least 5 times while rendering the list, as well as while rendering each item beyond the visible items. Sadly since notifyDatasetChanged()
method is final, you can't really override it, but you can call it from another function within the adapter.
此外,我不建议进行密集计算,getItemCount()
因为在呈现列表时以及在呈现可见项目之外的每个项目时,该特定函数至少被调用 5 次。遗憾的是,由于notifyDatasetChanged()
方法是最终的,您不能真正覆盖它,但您可以从适配器中的另一个函数调用它。
回答by Tulsi
It is very simple and straight forward.
这是非常简单和直接的。
Just Override getItemViewType()method in your adapter. On the basis of data return different itemViewType values. e.g Consider an object of type Person with a member isMale, if isMale is true, return 1 and isMale is false, return 2 in getItemViewType()method.
只需覆盖适配器中的getItemViewType()方法。根据数据返回不同的 itemViewType 值。例如,考虑一个具有成员 isMale 的 Person 类型的对象,如果 isMale 为真,则返回 1,而 isMale 为假,在getItemViewType()方法中返回 2 。
Now comes to the createViewHolder (ViewGroup parent, int viewType), on the basis of different viewType yon can inflate the different layout file. like the following
现在说到createViewHolder (ViewGroup parent, 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);
}
in onBindViewHolder (VH holder,int position)check where holder is instance of AdapterFemaleViewHolder
or AdapterMaleViewHolder
by instanceof
and accordingly assign the values.
在onBindViewHolder (VH holder,int position) 中检查 holder 是实例AdapterFemaleViewHolder
或AdapterMaleViewHolder
by 的位置,instanceof
并相应地分配值。
ViewHolder May be like this
ViewHolder 可能是这样的
class AdapterMaleViewHolder extends RecyclerView.ViewHolder {
...
public AdapterMaleViewHolder(View itemView){
...
}
}
class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
...
public AdapterFemaleViewHolder(View itemView){
...
}
}
回答by Vitaly
You can use the library: https://github.com/vivchar/RendererRecyclerViewAdapter
您可以使用该库: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 */
`
`
For each item, you should to implement a ViewRenderer, ViewHolder, SomeModel:
对于每个项目,您应该实现一个 ViewRenderer、ViewHolder、SomeModel:
ViewHolder - it is a simple view holder of recycler view.
ViewHolder - 它是一个简单的回收器视图的视图持有者。
SomeModel - it is your model with ItemModel
interface
SomeModel - 它是您的带有ItemModel
界面的模型
public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> {
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.
有关更多详细信息,您可以查看文档。
回答by Sayan Manna
Yes, it is possible. In your adapter getItemViewType Layout like this ....
对的,这是可能的。在您的适配器 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();
}
}
for reference link : https://www.journaldev.com/12372/android-recyclerview-example
参考链接:https: //www.journaldev.com/12372/android-recyclerview-example