Pon*_*pat 795 user-interface android android-recyclerview
来自https://developer.android.com/preview/material/ui-widgets.html
当我们创建时,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;
}
}
Run Code Online (Sandbox Code Playgroud)
那么,是否可以RecyclerView使用多种视图类型进行创建?
Ant*_*vin 1197
是的,这是可能的.只需实现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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
yqr*_*itc 83
如果视图类型的布局只有几个,绑定逻辑很简单,请遵循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();
......
}
Run Code Online (Sandbox Code Playgroud)
在创建单一视图类型时,此类中定义的函数与适配器类几乎完全相同.
对于每种视图类型,通过扩展此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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
要管理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);
......
}
Run Code Online (Sandbox Code Playgroud)
通过扩展此基类来创建类,然后实例化DataBinder类并覆盖抽象方法
getItemCount
返回DataBinders的项目总数
getItemViewType
定义适配器位置和视图类型之间的映射逻辑.
getDataBinder
根据视图类型返回DataBinder实例
getPosition
将转换逻辑定义为指定DataBinder中位置的适配器位置
getBinderPosition
从适配器位置将转换逻辑定义到DataBinder中的位置
希望这个解决方案会有所帮助.
我在GitHub中留下了更多细节解决方案和样本,如果需要,请参考以下链接.
https://github.com/yqritc/RecyclerView-MultipleViewTypesAdapter
Pha*_*inh 40
这是一个完整的示例,展示了具有两种类型的 RecyclerView,视图类型由对象决定。
类模型
open class RecyclerViewItem
class SectionItem(val title: String) : RecyclerViewItem()
class ContentItem(val name: String, val number: Int) : RecyclerViewItem()
Run Code Online (Sandbox Code Playgroud)
适配器代码
const val VIEW_TYPE_SECTION = 1
const val VIEW_TYPE_ITEM = 2
class UserAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var data = listOf<RecyclerViewItem>()
override fun getItemViewType(position: Int): Int {
if (data[position] is SectionItem) {
return VIEW_TYPE_SECTION
}
return VIEW_TYPE_ITEM
}
override fun getItemCount(): Int {
return data.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEW_TYPE_SECTION) {
return SectionViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_user_section, parent, false)
)
}
return ContentViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_user_content, parent, false)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = data[position]
if (holder is SectionViewHolder && item is SectionItem) {
holder.bind(item)
}
if (holder is ContentViewHolder && item is ContentItem) {
holder.bind(item)
}
}
internal inner class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: SectionItem) {
itemView.text_section.text = item.title
}
}
internal inner class ContentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: ContentItem) {
itemView.text_name.text = item.name
itemView.text_number.text = item.number.toString()
}
}
}
Run Code Online (Sandbox Code Playgroud)
item_user_section.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#eee"
android:padding="16dp" />
Run Code Online (Sandbox Code Playgroud)
item_user_content.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="32dp">
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Name" />
<TextView
android:id="@+id/text_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)
使用示例
val dataSet = arrayListOf<RecyclerViewItem>(
SectionItem("A1"),
ContentItem("11", 11),
ContentItem("12", 12),
ContentItem("13", 13),
SectionItem("A2"),
ContentItem("21", 21),
ContentItem("22", 22),
SectionItem("A3"),
ContentItem("31", 31),
ContentItem("32", 32),
ContentItem("33", 33),
ContentItem("33", 34),
)
recyclerAdapter.data = dataSet
recyclerAdapter.notifyDataSetChanged()
Run Code Online (Sandbox Code Playgroud)
Sim*_*mon 35
下面不是伪代码,我测试了它,它对我有用.
我想在我的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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
Isl*_*ssi 20
对的,这是可能的.
写一个通用视图持有者:
public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void setDataOnView(int position);
}
Run Code Online (Sandbox Code Playgroud)
然后创建视图持有者并使它们扩展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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
那么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);
}
Run Code Online (Sandbox Code Playgroud)
Gas*_*lén 18
比以往任何时候都简单,忘记 ViewTypes。不建议在一个适配器内使用多种视图类型。它会弄乱代码并破坏单一责任原则,因为现在适配器需要处理逻辑来知道要膨胀哪个视图。
\n现在想象一下在大型团队中工作,每个团队都必须使用这些视图类型功能之一。如果所有在不同视图类型中工作的团队都接触同一个适配器,将会很混乱。使用 ConcatAdapter 可以解决此问题,您可以在其中隔离适配器。对它们进行一一编码,然后将它们合并到一个视图中。
\n从recyclerview:1.2.0-alpha04现在起你就可以使用了ConcatAdapter。
如果您需要具有不同视图类型的视图,您可以为每个部分编写适配器,然后使用 ConcatAdapter 将所有它们合并到一个回收器视图中。
\n此图显示了一个回收视图具有的三种不同的视图类型:页眉、内容和页脚。
\n\n您只需为每个部分创建一个适配器,然后使用 ConcatAdapter 将它们合并到一个 recyclerview 中:
\nval firstAdapter: FirstAdapter = \xe2\x80\xa6\nval secondAdapter: SecondAdapter = \xe2\x80\xa6\nval thirdAdapter: ThirdAdapter = \xe2\x80\xa6\nval concatAdapter = ConcatAdapter(firstAdapter, secondAdapter,\n thirdAdapter)\nrecyclerView.adapter = concatAdapter\nRun Code Online (Sandbox Code Playgroud)\n\n这就是您需要知道的全部。如果您想处理加载状态,例如在加载发生后删除最后一个适配器,您可以使用LoadState。
\n有关更多深入信息,请关注 Florina Muntenescu 帖子https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a
\nRoh*_*ngh 16

RecyclerView可以拥有您想要的任意数量的视图,但为了更好的可读性,我们可以看看如何使用两个ViewHolders创建一个.
它可以通过三个简单的步骤完成
public int getItemViewType(int position)onCreateViewHolder()方法中的ViewType返回不同的ViewHoldersonBindViewHolder()方法中的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
}
}
}
Run Code Online (Sandbox Code Playgroud)
在我看来,创建这种recyclerView的起点是这种方法的知识.因为这个方法是可选的覆盖因此它默认在RecylerView类中不可见,这反过来又使许多开发人员(包括我)想知道从哪里开始.一旦你知道这个方法存在,创建这样的RecyclerView将是一个很小的步骤.
让我们看一个例子来证明我的观点.如果要在备用位置显示两个布局,请执行此操作
@Override
public int getItemViewType(int position)
{
if(position%2==0) // Even position
return LAYOUT_ONE;
else // Odd position
return LAYOUT_TWO;
}
Run Code Online (Sandbox Code Playgroud)
查看我已实现此项目的项目
Say*_*nna 12
对的,这是可能的.在您的适配器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();
}
}
Run Code Online (Sandbox Code Playgroud)
供参考链接:https://www.journaldev.com/12372/android-recyclerview-example
按照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);
}
Run Code Online (Sandbox Code Playgroud)
编辑: 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));
Run Code Online (Sandbox Code Playgroud)
如果没有找到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;
}
Run Code Online (Sandbox Code Playgroud)
虽然选择的答案是正确的,但我只想进一步阐述它。我在 RecyclerView 中为多种视图类型找到了一个有用的自定义适配器。它的Kotlin 版本在这里。
自定义适配器如下:
public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context;
ArrayList<String> list; // ArrayList of your Data Model
final int VIEW_TYPE_ONE = 1;
final int VIEW_TYPE_TWO = 2;
public CustomAdapter(Context context, ArrayList<String> list) { // you can pass other parameters in constructor
this.context = context;
this.list = list;
}
private class ViewHolder1 extends RecyclerView.ViewHolder {
TextView yourView;
ViewHolder1(final View itemView) {
super(itemView);
yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
}
void bind(int position) {
// This method will be called anytime a list item is created or update its data
// Do your stuff here
yourView.setText(list.get(position));
}
}
private class ViewHolder2 extends RecyclerView.ViewHolder {
TextView yourView;
ViewHolder2(final View itemView) {
super(itemView);
yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
}
void bind(int position) {
// This method will be called anytime a list item is created or update its data
//Do your stuff here
yourView.setText(list.get(position));
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ONE) {
return new ViewHolder1(LayoutInflater.from(context).inflate(R.layout.your_list_item_1, parent, false));
}
//if its not VIEW_TYPE_ONE then its VIEW_TYPE_TWO
return new ViewHolder2(LayoutInflater.from(context).inflate(R.layout.your_list_item_2, parent, false));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (list.get(position).type == Something) { // Put your condition, according to your requirements
((ViewHolder1) holder).bind(position);
} else {
((ViewHolder2) holder).bind(position);
}
}
@Override
public int getItemCount() {
return list.size();
}
@Override
public int getItemViewType(int position) {
// Here you can get decide from your model's ArrayList, which type of view you need to load. Like
if (list.get(position).type == Something) { // Put your condition, according to your requirements
return VIEW_TYPE_ONE;
}
return VIEW_TYPE_TWO;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我有一个更好的解决方案,允许以声明和类型安全的方式创建多个视图类型.它是用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
}
Run Code Online (Sandbox Code Playgroud)
有一个适配器数据项的抽象.请注意,视图类型由特定视图持有者类的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)
}
}
Run Code Online (Sandbox Code Playgroud)
只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() }
}
}
Run Code Online (Sandbox Code Playgroud)
这些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))
}
}
Run Code Online (Sandbox Code Playgroud)
简单的适配器类看起来像这样
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)
}
}
Run Code Online (Sandbox Code Playgroud)
创建新视图类型只需3个步骤:
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);
}
Run Code Online (Sandbox Code Playgroud)
在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){
...
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我推荐 Hannes Dorfmann 的这个库。它将与特定视图类型相关的所有逻辑封装在一个名为“AdapterDelegate”的单独对象中。
https://github.com/sockeqwe/AdapterDelegates
public class CatAdapterDelegate extends AdapterDelegate<List<Animal>> {
private LayoutInflater inflater;
public CatAdapterDelegate(Activity activity) {
inflater = activity.getLayoutInflater();
}
@Override public boolean isForViewType(@NonNull List<Animal> items, int position) {
return items.get(position) instanceof Cat;
}
@NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
}
@Override public void onBindViewHolder(@NonNull List<Animal> items, int position,
@NonNull RecyclerView.ViewHolder holder, @Nullable List<Object> payloads) {
CatViewHolder vh = (CatViewHolder) holder;
Cat cat = (Cat) items.get(position);
vh.name.setText(cat.getName());
}
static class CatViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public CatViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
}
}
}
public class AnimalAdapter extends ListDelegationAdapter<List<Animal>> {
public AnimalAdapter(Activity activity, List<Animal> items) {
// DelegatesManager is a protected Field in ListDelegationAdapter
delegatesManager.addDelegate(new CatAdapterDelegate(activity))
.addDelegate(new DogAdapterDelegate(activity))
.addDelegate(new GeckoAdapterDelegate(activity))
.addDelegate(23, new SnakeAdapterDelegate(activity));
// Set the items from super class.
setItems(items);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
385590 次 |
| 最近记录: |