【问题标题】:Android: how do I switch layouts for a RecyclerView list?Android:如何切换 RecyclerView 列表的布局?
【发布时间】:2016-04-28 11:20:09
【问题描述】:

我有一个默认布局,它在 RecyclerView 列表中包含一堆空白 CardView,基本上是一个欢迎屏幕,供用户向他们展示 CardView 的外观。然后用户启动一些数据的输入屏幕并单击“保存”按钮将数据保存到 CardView 中。用户单击保存后,布局应从具有空白 CardViews 的默认布局更改为包含用户数据的新的单个 CardView。稍后,如果用户删除了他们所有的 CardViews,那么视图应该切换回默认的空白 CardViews。

我正在努力解决如何在 onCreateViewHolder 中将代码设置为适配器,因为 getItemCount() 的默认值已经是正值(因为 RecyclerView 列表中已经有 4 或 5 个空白 CardView),这将一旦用户创建 4 或 5 个 CardView,稍后与相同的 getItemCount() 数量发生冲突。有关如何设置默认布局然后切换到新布局的任何想法,如果列表中的用户创建的 CardViews 清空,则可以恢复为默认布局?

以下是我在适配器中为两种布局布局测试的失败尝试。我意识到它不起作用,因为默认布局的 ItemCount 从来没有为零,因为已经有 4 或 5 个空白 CardView:

...
public class ContactAdapter extends RecyclerView.Adapter<ContactAdapter.ContactViewHolder> {

private List<ContactInfo> contactList;

public ContactAdapter(List<ContactInfo> contactList) {
    this.contactList = contactList;
}

@Override
public int getItemCount() {
    return contactList.size();
}

@Override
public ContactViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    if(contactList.size()== 0) {
        View itemView = LayoutInflater.
                from(viewGroup.getContext()).
                inflate(R.layout.defaultcard_layout, viewGroup, false);
        return new ContactViewHolder(itemView);
    }
    else {
        View itemView = LayoutInflater.
                from(viewGroup.getContext()).
                inflate(R.layout.singlecard_layout, viewGroup, false);
        return new ContactViewHolder(itemView);
    }
}

修改了Adapter和removeItem代码:

...
private LayoutInflater mLayoutInflater;
private List<Contact> mContacts;
private OnItemTapListener mOnItemTapListener;

public ListContactsAdapter(Context context, List<Contact> contacts) {
    Context mContext;
    mContext = context;
    mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mContacts = contacts;
}

public void removeItem(Contact contact, int position) {
    mContacts.remove(contact);
    if (mContacts.size()==0) {
        // if no more contacts in list,
        // we rebuild from scratch
        mContacts.clear();
        notifyDataSetChanged();
    } else {
        // else we just need to remove
        // one item
        mContacts.remove(position);
        notifyItemRemoved(position);
    }
}

【问题讨论】:

  • 你应该实现方法getItemViewType。这应该返回 2 作为值,因为您需要处理两种不同类型的视图。然后您应该能够确定每个列表元素的类型并将其用于构建布局。有关实现示例,请参阅here
  • 好的,我去看看。
  • 好的,让我知道这是否可行:为默认布局设置唯一的 ViewType。然后当用户单击保存创建一个新的 CardView 时,然后使用第二个布局。稍后,如果用户删除了第二个布局中的所有 CardView,则该 ViewType 的 getItemCount() 将为零,代码将切换到默认布局并刷新视图。
  • 我支持@thetonrifles 的建议。由于这两种布局的 itemCount 不同(首先您有 4、5 张空卡片,然后只有一张带有一些信息的卡片),您应该通过实现两种视图类型来区分这两种状态。将每种视图类型与不同的 ViewHolder 相关联,例如 EmptyCardViewHolder 用于第一个卡片状态,然后使用更好的名称用于 BigCardViewHolder。请注意,在您发布的代码中,onCreateViewHolder 的“int i”参数实际上是一个 viewType,您将使用它来决定哪个 ViewHolde
  • ...你应该回来。

标签: android android-layout android-recyclerview


【解决方案1】:

这是您可以遵循的方法:

  1. 为列表项(例如 ListItem)定义一个专用的抽象类型来包装您的业务对象。它的实现可能是这样的:

    public abstract class ListItem {
    
        public static final int TYPE_EMPTY = 0;
        public static final int TYPE_CONTACT = 1;
    
        abstract public int getType();
    } 
    
  2. 为每个 List 元素类型定义一个类:

    public class EmptyItem {
    
        @Override
        public int getType() {
            return TYPE_EMPTY;
        }
    
    }
    
    public class ContactItem {
    
        private ContactInfo contact;
    
        // here getters and setters 
        // for title and so on, built 
        // using contact
    
        public ContactItem(ContactInfo info) {
            this.contact = info;
        }
    
        @Override
        public int getType() {
            return TYPE_CONTACT;
        }
    
    }
    
  3. 创建您的列表。在下面的逻辑中,我只是确保您将始终拥有至少 5 个元素。如果您的联系人少于 5 个,则将显示空白布局。每次您从外部 Activity 修改您的 contactList 时,即使在 mContactList 中也可以进行此类修改,因为适配器保留了对 Activity 中管理的相同列表的引用(请参阅适配器构造函数)。例如,如果您添加了一个新联系人,则只需调用 updateContactList 方法来更新您的 UI。

    List<ContactInfo> mContactList;
    List<ListItem> mItems;
    
    public ContactsAdapter(List<ContactInfo> contactList) {
        mContactList = contactList;
        mItems = buildContactsList(mContactList);       
    }
    
    // Method for building ui list.
    private List<ContactItem> buildContactsList(List<ContactInfo> contactList) {
        List<ContactItem> list = new ArrayList<>();
        for (ContactInfo contact : contactList) {
            list.add(ContactItem(contact));
        }
        if (list.size() < 5) {
            for (int i=list.size(); i<5; i++) {
                list.add(EmptyItem());
            }
        }
    }
    
    // Method for updating contact list, providing
    // a new one. Everything to be build from scratch.
    public void updateContactsList() {
        mItems.clear();
        mItems.addAll(buildContactsList(mContactList));
        notifyDataSetChanged();
    }
    
  4. 为你的 RecyclerView 定义一个适配器,在第 3 点定义的 List 上工作。这里重要的是重写 getItemViewType 方法,如下所示:

    @Override
    public int getItemViewType(int position) {
        return mItems.get(position).getType();
    }
    

    然后你需要有两个布局和 ViewHolder 用于空和联系人项目。适配器方法应该相应地处理这个问题:

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == ListItem.TYPE_EMPTY) {
            View itemView = mLayoutInflater.inflate(R.layout.defaultcard_layout, parent, false);
            return new EmptyViewHolder(itemView);
        } else {
            View itemView = mLayoutInflater.inflate(R.layout.singlecard_layout, parent, false);
            return new ContactViewHolder(itemView);
        }
    }
    
    
    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
        int type = getItemViewType(position);
        if (type == ListItem.TYPE_EMPTY) {
            EmptyItem header = (EmptyItem) mItems.get(position);
            EmptyViewHolder holder = (EmptyViewHolder) viewHolder;
            // your logic here... probably nothing to do since it's empty
        } else {            
            ContactItem event = (ContactItem) mItems.get(position);
            ContactViewHolder holder = (ContactViewHolder) viewHolder;
            // your logic here
        }
    }
    

如果contactList 有更新,您当然应该相应地更新mItems,方法是清理它,使用第 3 点报告的相同逻辑再次填充它,然后在适配器上通知DataSetChanged。

【讨论】:

  • 非常酷,谢谢。我会在接下来的几个晚上有空的时候尝试实施这种方法,然后再回来查看。
  • 好的,但第 3 点可能不适合。当用户第一次到达 RecyclerView 屏幕时,他们会看到默认布局,即五张空白卡片(使用 CardView)。在他们单击工具栏上的“+”后,他们会创建一个新的自定义卡片并单击“保存”。然后我想在 RecyclerView 屏幕(第二个布局)上只显示他们新的单卡。用户可以继续添加卡片,布局将使用新卡片进行更新。如果用户删除了他们所有的卡片,我希望他们被送回他们看到的第一个屏幕(默认布局有五张空白卡片)。请指教。
  • 所以,第一个布局总是默认有五个空白卡片。五张固定牌。第二种布局从一张用户创建的卡片开始,然后卡片的数量可以增加或减少,但如果用户删除所有卡片,(我的想法:该 viewType 的 itemcount 达到零?)然后 RecyclerView 屏幕会返回以显示带有五张空白卡片的默认屏幕。
  • 我应该说使用 RecyclerView 的“getItemCount()”而不是上面的“itemcount”。
  • @AJW 我在第 3 点更新了代码,显示了适配器的可能实现。在适配器中保留对contactList 的引用,确保您始终拥有它是最新的。当您从外部添加或删除项目时,您只需在适配器上调用updateContactsList() 即可,ui 将自动更新。当然,甚至还有其他选择可以做到这一点。这只是一种可能的方法。让我知道一切是否清楚,或者我是否遗漏了您的 cmets 中的某些内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多