【问题标题】:RecyclerView Multiple Layout View(s) in an Adapter class适配器类中的 RecyclerView 多个布局视图
【发布时间】:2017-03-20 06:21:09
【问题描述】:

这是我所取得的成就? 3 个不同的部分,每个部分中有 10 个不同的项目

这是教程link我正在关注,下面是屏幕截图

尝试为每个部分显示different Views点赞:

对于第 1 节(layout_1.xml)

对于第 2 节(layout_2.xml)

对于第 3 节(layout_3.xml)

但是显示布局view layout_1.xml 在每个Section...(Section 1, 2, 3)

我可以知道我的代码哪里出错了,我错过了什么吗?

public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {

    private ArrayList<SingleItemModel> itemsList;
    private Context mContext;

    public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
        this.itemsList = itemsList;
        this.mContext = context;
    }

    @Override
    public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        switch (i) {

            case 0:
                View viewONE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
                SingleItemRowHolder rowONE = new SingleItemRowHolder(viewONE);
                return rowONE;

            case 1:
                View viewTWO = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
                SingleItemRowHolder rowTWO = new SingleItemRowHolder(viewTWO);
                return rowTWO;

            case 2:
                View viewTHREE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
                SingleItemRowHolder rowTHREE = new SingleItemRowHolder(viewTHREE);
                return rowTHREE;

        }

        return null;

    }

    @Override
    public void onBindViewHolder(SingleItemRowHolder holder, int i) {

        SingleItemModel singleItem = itemsList.get(i);
        holder.tvTitle.setText(singleItem.getName());
    }

    @Override
    public int getItemCount() {
        return (null != itemsList ? itemsList.size() : 0);
    }

    public class SingleItemRowHolder extends RecyclerView.ViewHolder {

        protected TextView tvTitle;

        protected ImageView itemImage;


        public SingleItemRowHolder(View view) {
            super(view);

            this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
            this.itemImage = (ImageView) view.findViewById(R.id.itemImage);

            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();

                }
            });

        }

    }

}

【问题讨论】:

  • int i 0 一直
  • 覆盖 itemviewtype
  • @IntelliJAmiya 是的,你是绝对正确的......总是得到 0......我该如何解决这个问题
  • 为什么不使用三个不同的回收器视图和三个不同的适配器

标签: android xml layout android-recyclerview adapter


【解决方案1】:

在适配器的getItemViewType 中使用这个:

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

【讨论】:

  • position 0开始
  • 然后根据你的需要改变
  • 可能是完美的答案。
  • 试过了,但它改变了各自的位置......而不是各自的部分......我需要明智地得到同样的东西......
  • 你需要不同的viewHolders
【解决方案2】:

要根据 recyclerview 中的位置使用多个布局,您必须覆盖适配器内的 getItemViewType(int position) 方法:-

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

【讨论】:

    【解决方案3】:

    仅供参考

    RecyclerView 也可用于膨胀多种视图类型。

    • 创建不同的 Holder 对您来说是最简单的。
    • 创建不同的适配器是最好的解决方案

    试试

      @Override
            public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    
                switch (i) {
    
                    case 0:
                        View viewONE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
                        SingleItemRowHolder rowONE = new SingleItemRowHolder(viewONE);
                        return rowONE;
    
                    case 1:
                        View viewTWO = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
                        SingleItemRowHolderTwo rowTWO = new SingleItemRowHolderTwo (viewTWO);
                        return rowTWO;
    
                    case 2:
                        View viewTHREE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
                        SingleItemRowHolderThree rowTHREE = new SingleItemRowHolderThree(viewTHREE);
                        return rowTHREE;
    
                }
    
                return null;
    
            }
    

    阅读RecyclerView can also be used to inflate multiple view types

    【讨论】:

    • 这是新的适配器类,我创建以使用不同的 ViewHolders...但仍然得到相同的结果:pastebin.com/8mqPJmAP
    • @Sophie 没有。创建 3 个不同的适配器类,例如 (SectionListDataAdapter)。
    • @Sophie 有什么消息吗?
    • 没有没有成功:(终于开始赏金了:)
    【解决方案4】:

    正如已经提到的那样,为了 RecyclerView.Adapter 类的 getItemViewType 方法,因为如果您将在该方法的实现中看到,您会看到它总是返回 0

        public int getItemViewType(int position) {
            return 0;
        }
    

    这里调整了您的适配器的代码,应该可以解决您的问题。

    public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
        private static final int ITEM_TYPE_ROW_1 = 0;
        private static final int ITEM_TYPE_ROW_2 = 1;
        private static final int ITEM_TYPE_ROW_3 = 2;
    
        private ArrayList<SingleItemModel> itemsList;
        private Context context;
    
        public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
            this.itemsList = itemsList;
            this.context = context;
        }
    
        @Override
        public int getItemViewType(int position) {
            switch (position) {
                case 0:
                    return ITEM_TYPE_ROW_1;
                case 1:
                    return ITEM_TYPE_ROW_2;
                case 2:
                    return ITEM_TYPE_ROW_3;
            }
            throw new RuntimeException(String.format("unexpected position - %d", position));
        }
    
        @Override
        public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
            switch (viewType) {
                case ITEM_TYPE_ROW_1:
                    View viewOne = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
                    return new SingleItemRowHolder(viewOne);
                case ITEM_TYPE_ROW_2:
                    View viewTwo = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
                    return new SingleItemRowHolder(viewTwo);
                case ITEM_TYPE_ROW_3:
                    View viewThree = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
                    return new SingleItemRowHolder(viewThree);
            }
    
            throw new RuntimeException(String.format("unexpected viewType - %d", viewType));
        }
    
        @Override
        public void onBindViewHolder(SingleItemRowHolder holder, int i) {
            SingleItemModel singleItem = itemsList.get(i);
            holder.tvTitle.setText(singleItem.getName());
        }
    
        @Override
        public int getItemCount() {
            return (null != itemsList ? itemsList.size() : 0);
        }
    
        class SingleItemRowHolder extends RecyclerView.ViewHolder {
            TextView tvTitle;
            ImageView itemImage;
    
            public SingleItemRowHolder(View view) {
                super(view);
    
                this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
                this.itemImage = (ImageView) view.findViewById(R.id.itemImage);
    
                view.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      你可以用更简单的方法来做。在初始化适配器时传递任何flag

      public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
          private ArrayList<SingleItemModel> itemsList;
          private Context context;
          private int view;
      
          public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList, int layoutFlag) {
              this.itemsList = itemsList;
              this.context = context;
              switch(layoutFlag) {
                 case 0:
                     view = R.layout.layout_1;
                     break;
                 case 1:
                     view = R.layout.layout_2;
                     break;
                 case 2:
                     view = R.layout.layout_3;
                     break;
              }
          }
      ...
      ...
      ...
      }
      

      将此view 用于布局参考。您只需要在设置适配器时告知要膨胀的布局即可。

      【讨论】:

        【解决方案6】:

        你需要重写方法

        int getItemViewType (int position)
        

        它接收行号,您需要返回行的“类型”,即 1 2 或 3。

        然后将结果传递给 onCreateViewHolder。

        【讨论】:

          【解决方案7】:

          例如,如果您想显示此视图列表:

          type1 类型2 类型3 类型1 类型2 类型3

          那应该做的工作:

          public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
              private static final int ITEM_TYPE_ROW_1 = 0;
              private static final int ITEM_TYPE_ROW_2 = 1;
              private static final int ITEM_TYPE_ROW_3 = 2;
          
              private ArrayList<SingleItemModel> itemsList;
              private Context context;
              private ArrayList<Integer> viewTypes = new ArrayList<>();
          
              public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
                  this.itemsList = itemsList;
                  this.context = context;
          
                  viewTypes.add(ITEM_TYPE_ROW_1);
                  viewTypes.add(ITEM_TYPE_ROW_2);
                  viewTypes.add(ITEM_TYPE_ROW_3);
                  viewTypes.add(ITEM_TYPE_ROW_1);
                  viewTypes.add(ITEM_TYPE_ROW_2);
                  viewTypes.add(ITEM_TYPE_ROW_3);
          
              }
          
              @Override
              public int getItemViewType(int position) {
                 return viewTypes.get(position);            
              }
          
              @Override
              public int getItemCount() {
                  return viewTypes.size();
              }
          
          
              .......
              ........
          

          如果您想添加/删除行,则可以在 viewTypes 数组中插入/删除 viewTypes,然后调用 RecyclerView notifyItemInserted 或 notifyItemRemoved 方法,列表将使用新的顺序和类型进行更新观看次数。

          【讨论】:

            【解决方案8】:

            只需将框架布局与您的片段一起使用,并将此片段添加到您的框架布局中,它将按照您的需要添加。所以它也很容易处理。希望对你有帮助

            【讨论】:

              【解决方案9】:

              是的,您需要重写 getItemViewType(int position) 方法,这有助于在 recyclerview 中膨胀不同的视图。

              我发布了一个示例代码,可能会对您有所帮助。

              public class TransactionHistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
              private final int TYPE_HEADER = 1;
              private final int TYPE_CHILD = 2;
              private final Context mContext;
              private final List<TransactionResultEntity> mTransactionList;
              
              
              @Override
              public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                  switch (viewType) {
                      case TYPE_HEADER:
                          View headerView = LayoutInflater.from(mContext)
                                  .inflate(R.layout.row_transaction_header, parent, false);
                          return new ParentTypeDataObjectHolder(headerView);
                      case TYPE_CHILD:
                          View childView = LayoutInflater.from(mContext)
                                  .inflate(R.layout.row_transaction_child, parent, false);
                          return new ChildTypeDataObjectHolder(childView);
                  }
                  return null;
              }
              
              @Override
              public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                  switch (holder.getItemViewType()) {
                      case TYPE_HEADER:
                          ParentTypeDataObjectHolder parentTypeDataObjectHolder = (ParentTypeDataObjectHolder) holder;
                          parentTypeDataObjectHolder.headerYearMonthTv.setText(mTransactionList.get(holder.getAdapterPosition()).getRowLabel());
                          break;
              
                      case TYPE_CHILD:
                          ChildTypeDataObjectHolder childTypeDataObjectHolder = (ChildTypeDataObjectHolder) holder;
                         childTypeDataObjectHolder.txnAmountTv.setText(mTransactionList.get(holder.getAdapterPosition()).getTransactionAmount());
                          break;
                  }
              }
              
              
              @Override
              public int getItemCount() {
                  return mTransactionList.size();
              }
              
              @Override
              public int getItemViewType(int position) {
                  if (mTransactionList.get(position).getDataType() == TYPE_HEADER)
                      return TYPE_HEADER;
                  else
                      return TYPE_CHILD;
              }
              
              class ParentTypeDataObjectHolder extends RecyclerView.ViewHolder {
              
                  private final TextView headerYearMonthTv;
              
                  public ParentTypeDataObjectHolder(View itemView) {
                      super(itemView);
                      headerYearMonthTv = (TextView) itemView.findViewById(R.id.row_transaction_header_tv);
                  }
              }
              
              class ChildTypeDataObjectHolder extends RecyclerView.ViewHolder {
                  TextView txnAmountTv;
              
                  public ChildTypeDataObjectHolder(View itemView) {
                      super(itemView);
                      txnAmountTv = (TextView) itemView.findViewById(R.id.transaction_child_txn_amount_tv);
                              }
              }
              

              }

              【讨论】:

                【解决方案10】:

                您需要做的就是覆盖适配器内部的方法getItemViewType()

                你可以写成:

                 @Override
                    public int getItemViewType(int position) {
                        if (position < 0) {
                            return 0;
                        } else if(position < 20) {
                            return 1;
                        } else {
                          return 2;
                        }
                    }
                

                现在,如果您的 itemsList ArrayList 有第 1 节的前 10 项、第 2 节的后 10 项和第 3 节的最后 10 项,则上述逻辑有效。

                如果不是这种情况,那么您可以在 SingleItemModel 类中拥有一个整数字段 sectionNumber,它指定该模型所属的节号。现在您可以将方法getItemViewType()修改为

                @Override
                public int getItemViewType(int position) {
                    SingleItemModel singleItemModel = itemsList.get(position);
                    if (singleItemModel.getSection() == 1) {
                        return 0;
                    } else if(singleItemModel.getSection() == 2) {
                        return 1;
                    } else {
                      return 2;
                    }
                }
                

                【讨论】:

                  【解决方案11】:

                  好的,如果我没记错的话,你想制作第二个适配器,即提供行列表的那个,变量,所以它支持不同的布局,不是基于它的位置,而是基于来自主适配器的一些数据(一个提供部分)。因此,覆盖 getItemViewType 将不起作用,因为部分数据包含在主适配器中,它甚至无法到达那里。因此,最好、最简洁的方法是使用... abstraction。忘记多个viewholders。使用一个,并在其中绑定一个自定义视图。自定义视图将提供特定的布局文件并设置其中包含的控件。持有者将做它打算做的事情:通过重用视图来节省内存。这样做的好处是,您可以拥有一个干净的层次结构,它可以随着时间的推移变得复杂,而不是一个又大又胖的适配器,这将变得难以维护。这里是:

                  由于这里有很多代码,因此我采用了您的示例项目并进行了修改以提供我理解的您正在尝试做的事情。这里是:

                  https://github.com/fcopardo/exampleCustomViewsInHolder/tree/master

                  亮点:

                  public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
                  
                      private ArrayList<SingleItemModel> itemsList;
                      private Context context;
                      private String section;
                  
                      public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList, String sectionName) {
                          this.itemsList = itemsList;
                          this.context = context;
                          this.section = sectionName;
                      }
                  
                      @Override
                      public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
                          return new SingleItemRowHolder(RowFactory.getRow(context, section));
                      }
                  
                      @Override
                      public void onBindViewHolder(SingleItemRowHolder holder, int i) {
                          holder.setData(itemsList.get(i));
                      }
                  
                      @Override
                      public int getItemCount() {
                          return (null != itemsList ? itemsList.size() : 0);
                      }
                  
                      public class SingleItemRowHolder extends RecyclerView.ViewHolder {
                  
                          protected AbstractRowElement rowElement;
                  
                          public SingleItemRowHolder(AbstractRowElement view) {
                              super(view);
                              this.rowElement = view;
                          }
                  
                          public void setData(SingleItemModel singleItemModel){
                              rowElement.setItem(singleItemModel);
                          }
                  
                      }
                  
                  
                  
                  }
                  

                  这是可变布局适配器。如您所见,它仅使用一个 ViewHolder 和一个工厂来提供您需要的视图实例。

                  public class RowFactory {
                  
                      public static AbstractRowElement getRow(Context context, String name){
                          switch (name){
                              case "Section 1": return new FullRowElement(context);
                              case "Section 2": return new TextRowElement(context);
                              case "Section 3": return new ImageRowElement(context);
                              default:
                                  Log.e("inflate", name);
                                  return new FullRowElement(context);
                          }
                      }
                  }
                  

                  这提供了自定义视图,每个视图使用不同的布局,但使用相同的数据集,基于部分标题。

                  public abstract class AbstractRowElement extends CardView{
                  
                      protected int layout = 0;
                      protected SingleItemModel singleItemModel;
                  
                      public AbstractRowElement(Context context) {
                          super(context);
                          inflateBaseLayout();
                      }
                  
                      public AbstractRowElement(Context context, AttributeSet attrs) {
                          super(context, attrs);
                          inflateBaseLayout();
                      }
                  
                      public AbstractRowElement(Context context, AttributeSet attrs, int defStyleAttr) {
                          super(context, attrs, defStyleAttr);
                          inflateBaseLayout();
                      }
                  
                      protected void inflateBaseLayout() {
                          this.setContainer();
                          if(this.layout != 0) {
                              LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                              inflater.inflate(layout, this, true);
                              this.inflateComponents();
                          }
                      }
                  
                      protected abstract void setContainer();
                      protected abstract void inflateComponents();
                  
                      public void setItem(SingleItemModel itemModel){
                          this.singleItemModel = itemModel;
                          this.setOnClickListener(new OnClickListener() {
                              @Override
                              public void onClick(View v) {
                                  Toast.makeText(getContext(), singleItemModel.getName()+"\n"+singleItemModel.getDescription(), Toast.LENGTH_SHORT).show();
                              }
                          });
                          setData(singleItemModel);
                      }
                      public abstract void setData(SingleItemModel itemModel);
                  
                  }
                  

                  最后,这是适配器的基本视图类。子类定义要使用的布局文件,并将所需的数据放入控件中。其余的都很简单。

                  完全有可能在没有自定义视图的情况下做到这一点。你可以做类似的东西:

                  int layoutFile = getLayoutForSection(section);
                  View v = LayoutInflater.from(viewGroup.getContext()).inflate(layoutFile, null);
                  

                  但是由于我不知道您打算创建的视图有多复杂,所以最好将它们很好地分开。玩得开心!

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-01-13
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-01-05
                    • 2021-05-04
                    • 1970-01-01
                    相关资源
                    最近更新 更多