使用RecyclerView创建动态列表:

当我们创建一个RecyclerView时。适配器我们必须指定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;
    }
}

有可能创建多个视图类型的RecyclerView吗?


当前回答

为不同的布局创建不同的ViewHolder

RecyclerView可以有任意数量的viewholders,但为了更好的可读性,让我们看看如何创建一个有两个viewholders的viewholders。

这可以通过三个简单的步骤来完成

覆盖公共int getItemViewType(int position) 在onCreateViewHolder()方法中根据ViewType返回不同的ViewHolders 根据onBindViewHolder()方法中的itemViewType填充视图

下面是一小段代码片段:

public class YourListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

   private static final int LAYOUT_ONE = 0;
   private static final int LAYOUT_TWO = 1;

   @Override
   public int getItemViewType(int position)
   {
      if(position==0)
        return LAYOUT_ONE;
      else
        return LAYOUT_TWO;
   }

   @Override
   public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

      View view = null;
      RecyclerView.ViewHolder viewHolder = null;

      if(viewType==LAYOUT_ONE)
      {
          view = LayoutInflater.from(parent.getContext()).inflate(R.layout.one,parent,false);
          viewHolder = new ViewHolderOne(view);
      }
      else
      {
          view = LayoutInflater.from(parent.getContext()).inflate(R.layout.two,parent,false);
          viewHolder= new ViewHolderTwo(view);
      }

      return viewHolder;
   }

   @Override
   public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {

      if(holder.getItemViewType() == LAYOUT_ONE)
      {
            // Typecast Viewholder
            // Set Viewholder properties
            // Add any click listener if any
      }
      else {
        ViewHolderOne vaultItemHolder = (ViewHolderOne) holder;
        vaultItemHolder.name.setText(displayText);
        vaultItemHolder.name.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
            .......
           }
         });
       }
   }

  //****************  VIEW HOLDER 1 ******************//

   public class ViewHolderOne extends RecyclerView.ViewHolder {

      public TextView name;

      public ViewHolderOne(View itemView) {
         super(itemView);
         name = (TextView)itemView.findViewById(R.id.displayName);
     }
   }


   //****************  VIEW HOLDER 2 ******************//

   public class ViewHolderTwo extends RecyclerView.ViewHolder {

      public ViewHolderTwo(View itemView) {
         super(itemView);

        ..... Do something
      }
   }
}

getItemViewType(int position)是键。

在我看来,创建这种recyclerView的起点是这个方法的知识。因为这个方法是可选的,所以默认情况下它在RecylerView类中是不可见的,这使得许多开发人员(包括我)不知道从哪里开始。

一旦您知道了这个方法的存在,创建这样的RecyclerView将是小菜一菜。

让我们看一个例子来证明我的观点。如果你想显示两个布局 在交替的位置上这样做

@Override
public int getItemViewType(int position)
{
   if(position%2==0)       // Even position
     return LAYOUT_ONE;
   else                   // Odd position
     return LAYOUT_TWO;
}

相关链接:

看看我已经实现了这个项目。

其他回答

实际上,我想改进一下安东的回答。

由于getItemViewType(int position)返回一个整数值,您可以返回需要膨胀的布局资源ID。这样你就可以在onCreateViewHolder(ViewGroup parent, int viewType)方法中保存一些逻辑。

此外,我不建议在getItemCount()中进行密集的计算,因为在呈现列表时,以及在呈现可见项之外的每个项时,该特定函数至少被调用了5次。遗憾的是,由于notifyDatasetChanged()方法是final方法,所以不能重写它,但是可以从适配器中的另一个函数调用它。

如果你想将它与Android数据绑定结合使用,请查看https://github.com/evant/binding-collection-adapter -它是迄今为止我所见过的多种视图类型RecyclerView的最佳解决方案。

你可以这样使用它

var items: AsyncDiffPagedObservableList<BaseListItem> =
        AsyncDiffPagedObservableList(GenericDiff)

    val onItemBind: OnItemBind<BaseListItem> =
        OnItemBind { itemBinding, _, item -> itemBinding.set(BR.item, item.layoutRes) }

然后在列表所在的布局中

 <androidx.recyclerview.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                app:enableAnimations="@{false}"
                app:scrollToPosition="@{viewModel.scrollPosition}"

                app:itemBinding="@{viewModel.onItemBind}"
                app:items="@{viewModel.items}"

                app:reverseLayoutManager="@{true}"/>

你的列表项必须实现BaseListItem接口,它看起来像这样:

interface BaseListItem {
    val layoutRes: Int
}

项目视图应该看起来像这样:

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
                name="item"
                type="...presentation.somescreen.list.YourListItem"/>
    </data>

   ...

</layout>

YourListItem实现BaseListItem。

首先,必须创建两个布局XML文件。之后,在recyclerview适配器中,TYPE_CALL和TYPE_EMAIL是两个静态值,分别为适配器类中的1和2。

现在在Recycler视图Adapter类级别定义两个静态值,例如:private static int TYPE_EMAIL = 2;

现在创建多个视图的视图持有者,如下所示:

class CallViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    CallViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}

class EmailViewHolder extends RecyclerView.ViewHolder {

    private TextView txtName;
    private TextView txtAddress;

    EmailViewHolder(@NonNull View itemView) {
        super(itemView);
        txtName = itemView.findViewById(R.id.txtName);
        txtAddress = itemView.findViewById(R.id.txtAddress);
    }
}

现在在recyclerview适配器的onCreateViewHolder和onBindViewHolder方法中编写如下代码:

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
    View view;
    if (viewType == TYPE_CALL) { // for call layout
        view = LayoutInflater.from(context).inflate(R.layout.item_call, viewGroup, false);
        return new CallViewHolder(view);

    } else { // for email layout
        view = LayoutInflater.from(context).inflate(R.layout.item_email, viewGroup, false);
        return new EmailViewHolder(view);
    }
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
    if (getItemViewType(position) == TYPE_CALL) {
        ((CallViewHolder) viewHolder).setCallDetails(employees.get(position));
    } else {
        ((EmailViewHolder) viewHolder).setEmailDetails(employees.get(position));
    }
}

比以往任何时候都简单,忘记ViewTypes。不建议在一个适配器中使用多个视图类型。这将使代码混乱,并打破单一责任原则,因为现在适配器需要处理逻辑来知道要填充哪个视图。

现在,想象一下在大型团队中工作,每个团队都必须在这些视图类型中的一种功能中工作。如果在不同视图类型中工作的所有团队都使用同一个适配器,那将是一团糟。这是使用ConcatAdapter解决的,其中隔离了适配器。逐个编码,然后将它们合并到一个视图中。

在recyclerview:1.2.0-alpha04中,您现在可以使用ConcatAdapter。

如果你需要一个具有不同viewTypes的视图,你可以为每个部分编写适配器,并使用ConcatAdapter将它们合并到一个recyclerview中。

ConcatAdapter

这张图片显示了一个recyclerview拥有的三种不同的视图类型,页眉,内容和页脚。

你只需要为每个section创建一个适配器,然后使用ConcatAdapter将它们合并到一个recyclerview中:

val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val concatAdapter = ConcatAdapter(firstAdapter, secondAdapter,
                                  thirdAdapter)
recyclerView.adapter = concatAdapter

这就是你需要知道的。如果您想处理加载状态,例如在某些加载发生后删除最后一个适配器,您可以使用LoadState。

欲了解更多深入信息,请关注Florina Muntenescu的帖子https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a

我是这样做的。我传递了“fragmentType”,并创建了两个ViewHolders,在此基础上,我在一个适配器中相应地分类了我的布局,可以有不同的布局和布局管理器

private Context mContext;
protected IOnLoyaltyCardCategoriesItemClicked mListener;
private String fragmentType;
private View view;

public LoyaltyCardsCategoriesRecyclerViewAdapter(Context context, IOnLoyaltyCardCategoriesItemClicked itemListener, String fragmentType) {
    this.mContext = context;
    this.mListener = itemListener;
    this.fragmentType = fragmentType;
}

public class LoyaltyCardCategoriesFragmentViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    private ImageView lc_categories_iv;
    private TextView lc_categories_name_tv;
    private int pos;

    public LoyaltyCardCategoriesFragmentViewHolder(View v) {
        super(v);
        view.setOnClickListener(this);
        lc_categories_iv = (ImageView) v.findViewById(R.id.lc_categories_iv);
        lc_categories_name_tv = (TextView) v.findViewById(R.id.lc_categories_name_tv);
    }

    public void setData(int pos) {
        this.pos = pos;
        lc_categories_iv.setImageResource(R.mipmap.ic_launcher);
        lc_categories_name_tv.setText("Loyalty Card Categories");
    }

    @Override
    public void onClick(View view) {
        if (mListener != null) {
            mListener.onLoyaltyCardCategoriesItemClicked(pos);
        }
    }
}

public class MyLoyaltyCardsFragmentTagViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    public ImageButton lc_categories_btn;
    private int pos;

    public MyLoyaltyCardsFragmentTagViewHolder(View v) {
        super(v);
        lc_categories_btn = (ImageButton) v.findViewById(R.id.lc_categories_btn);
        lc_categories_btn.setOnClickListener(this);
    }

    public void setData(int pos) {
        this.pos = pos;
        lc_categories_btn.setImageResource(R.mipmap.ic_launcher);
    }

    @Override
    public void onClick(View view) {
        if (mListener != null) {
            mListener.onLoyaltyCardCategoriesItemClicked(pos);
        }
    }
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (fragmentType.equalsIgnoreCase(Constants.LoyaltyCardCategoriesFragmentTag)) {
        view = LayoutInflater.from(mContext).inflate(R.layout.loyalty_cards_categories_frag_item, parent, false);
        return new LoyaltyCardCategoriesFragmentViewHolder(view);
    } else if (fragmentType.equalsIgnoreCase(Constants.MyLoyaltyCardsFragmentTag)) {
        view = LayoutInflater.from(mContext).inflate(R.layout.my_loyalty_cards_categories_frag_item, parent, false);
        return new MyLoyaltyCardsFragmentTagViewHolder(view);
    } else {
        return null;
    }
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    if (fragmentType.equalsIgnoreCase(Constants.LoyaltyCardCategoriesFragmentTag)) {
        ((LoyaltyCardCategoriesFragmentViewHolder) holder).setData(position);
    } else if (fragmentType.equalsIgnoreCase(Constants.MyLoyaltyCardsFragmentTag)) {
        ((MyLoyaltyCardsFragmentTagViewHolder) holder).setData(position);
    }
}

@Override
public int getItemCount() {
    return 7;
}