RecyclerView在Adapter类中使用多个布局视图

7
这是我所实现的内容:共有3个不同的部分,每个部分有10个不同的项目
这是我正在跟随的教程链接,以下是截图enter image description here 尝试为每个部分显示不同的视图。像这样:
对于第 1 部分 (layout_1.xml)
对于第 2 部分 (layout_2.xml)
对于第 3 部分 (layout_3.xml)
但是在每个部分(第 1、2、3 部分)中都显示了layout_1.xml的布局视图请问我的代码哪里出错了,我漏掉了什么?
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();

                }
            });

        }

    }

}

1
重写itemviewtype - Divyesh Patel
1
@IntelliJAmiya,你说得完全正确。我总是得到0... 我该如何解决这个问题? - Sophie
为什么不使用三个不同的Recycler View和三个不同的Adapter呢? - Abhishek Singh
这是一个有些复杂的问题,有一些库可以比这些答案更好地解决它。我个人使用 https://github.com/airbnb/epoxy,但也有 https://github.com/mikepenz/FastAdapter 或 https://github.com/sockeqwe/AdapterDelegates。我建议查看这些库,看看哪一个适合您的需求。 - James McCracken
我以前成功地完成了这个任务:创建一个带有自定义对象的ArrayList,并将ArrayList的每个项目加载到另一个ArrayList中,然后将其设置为两个自定义Recycler View,一个垂直的,另一个水平的。 - g7pro
显示剩余3条评论
11个回答

8

在适配器的 getItemViewType 中使用:

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

1
position starts from 0 - IntelliJ Amiya
1
请将以下程序相关内容从英语翻译为中文。仅返回已翻译的文本:然后根据您的需求进行更改。 - Divyesh Patel
可能是完美的答案。 - IntelliJ Amiya
尝试过了,但它只是针对相应的位置进行更改...而不是相应的部分...我需要按部分获得相同的结果... - Sophie
你需要不同的viewHolders。 - Divyesh Patel
显示剩余4条评论

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

1
你可以更简单地完成它。在初始化适配器时传递任何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;
        }
    }
...
...
...
}

使用此视图作为布局参考。您只需要在设置适配器时告诉要填充哪个布局即可。

1
正如已经提到的那样,为了使用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();
                }
            });
        }
    }

}

1

注意

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也可以用来填充多个视图类型


1
这是我创建的新适配器类,用于使用不同的ViewHolders...但仍然得到相同的结果:pastebin.com/8mqPJmAP - Sophie
@Sophie 不要。创建3个不同的适配器类,例如(SectionListDataAdapter)。 - IntelliJ Amiya
@Sophie 有什么新消息吗? - IntelliJ Amiya
没有成功 :( 最终开始悬赏 :) - Sophie

0

只需在您的片段中使用框架布局,并将该片段添加到您的框架布局中,就可以按照您想要的方式添加。因此,这也很容易处理。希望这能帮助您。


0
如果你想显示这个视图列表,例如:
type1 type2 type3 type1 type2 type3
那么应该用以下方法实现:
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 数组中插入/删除视图类型,然后调用 RecyclerView 的 notifyItemInserted 或 notifyItemRemoved 方法,列表将会更新为新的视图顺序和类型。

0

是的,您需要重写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);
                }
}

}


0
你所需要做的就是在你的适配器中重写方法getItemViewType()
你可以这样写:
 @Override
    public int getItemViewType(int position) {
        if (position < 0) {
            return 0;
        } else if(position < 20) {
            return 1;
        } else {
          return 2;
        }
    }

如果你的itemsListArrayList中有第一部分的前10个项目,第二部分的下一个10个项目和第三部分的最后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;
    }
}

0

好的,如果我理解正确的话,您想要使第二个适配器(提供行列表)可变,以便它支持不基于其位置而是基于主适配器(提供节目)中的一些数据的不同布局。因此,覆盖getItemViewType将不起作用,因为节数据包含在主适配器中,它甚至无法到达那里。因此,最好且最清晰的方法是使用... 抽象化。忘记多个视图持有者。只使用一个,并将自定义视图绑定到其中。自定义视图将提供特定的布局文件,并设置其中包含的控件。持有者将只做其旨在做的事情:通过重用视图来保存RAM。这样做的优点是,您可以拥有一个干净的层次结构,它可以随着时间的推移而变得更加复杂,而不是一个大而臃肿的适配器,这将变得太难以维护。这就是:

由于这是很多代码放在这里,我使用了您的示例项目并进行了修改,以提供我理解您尝试执行的操作。这就是它:

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

但是,由于我不知道您打算创建的视图有多复杂,最好将事情保持得井井有条。玩得开心!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接