使用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吗?


当前回答

我首先推荐你阅读Hannes Dorfmann关于这个主题的优秀文章。

当一个新的视图类型出现时,你必须编辑适配器,你必须处理这么多乱七八糟的事情。您的适配器应该对扩展开放,但对修改关闭。

你可以看看这两个项目,他们可以给出如何在适配器中处理不同的ViewTypes的想法:

https://github.com/sockeqwe/AdapterDelegates https://github.com/ibrahimyilmaz/kiel

其他回答

如果有人有兴趣看看用Kotlin编写的超级简单的解决方案,请查看我刚刚创建的博客文章。博客中的例子是基于创建Sectioned RecyclerView的:

https://brona.blog/2020/06/sectioned-recyclerview-in-three-steps/

使用Kotlin,视图类型的实现变得更加容易。下面是这个light库https://github.com/Link184/KidAdapter的示例

recyclerView.setUp {
    withViewType {
        withLayoutResId(R.layout.item_int)
        withItems(mutableListOf(1, 2, 3, 4, 5, 6))
        bind<Int> { // this - is adapter view hoder itemView, it - current item
            intName.text = it.toString()
        }
    }


    withViewType("SECOND_STRING_TAG") {
        withLayoutResId(R.layout.item_text)
        withItems(mutableListOf("eight", "nine", "ten", "eleven", "twelve"))
        bind<String> {
            stringName.text = it
        }
    }
}

为不同的布局创建不同的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方法,所以不能重写它,但是可以从适配器中的另一个函数调用它。

是的,这是可能的。只需实现getItemViewType(),并照顾onCreateViewHolder()中的viewType参数。

所以你可以这样做:

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