【问题标题】:How to handle expandable ViewHolders in a RecyclerView?如何处理 RecyclerView 中的可扩展 ViewHolders?
【发布时间】:2019-04-05 20:59:50
【问题描述】:

我有一个 RecyclerView 在我的应用程序中使用 FirestoreRecyclerAdapterViewHolders 实现了一个可扩展的“快速操作”菜单,可在点击项目时展开,理想的行为是一次只展开一个项目。展开一项应折叠适配器中的所有其他项。

当屏幕上的项目足够少时,这可以正常工作。但是,如果列表在屏幕外扩展,当我尝试遍历适配器的项目时,当分离或屏幕外的 ViewHolders 尝试调用 getParent() 时,我会得到 NullPointerException

如何遍历适配器中的所有 ViewHolders,或者仅遍历可见的 ViewHolders?

相关适配器代码:

@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder, 
               int position, @NonNull Brew brew) {

    // Bind Views
    holder.bind(brew);

    // Set expander ClickListener
    holder.card.setOnClickListener(v -> {
        for (int i = 0; i < mAdapter.getItemCount(); i++) {
            if (i != position) {
                // ERROR THROWN HERE
                BrewViewHolder vh = ((BrewViewHolder) recyclerView.getChildViewHolder(recyclerView.getChildAt(i)));
                vh.expanded = false;
            }
        }
        holder.expanded = !holder.expanded;
        notifyItemChanged(position);
   });

}

相关ViewHolder代码:

public class BrewViewHolder extends RecyclerView.ViewHolder {

    // Expanded state
    public boolean expanded = false;
    ...

    public BrewViewHolder(@NonNull final View itemView) {
        super(itemView);
        ...
        // Set visibility of quick actions based on expanded state
        quickActions.setVisibility(expanded ? View.VISIBLE : View.GONE);
    }

}

提前致谢!

编辑:正如Gilberto 指出的那样,这非常昂贵。使用Andrew的解决方案版本:

private int expandedItemIndex = -1;

@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder, int position,
                                @NonNull Brew brew) {
    // Bind Views
    holder.bind(brew);

    // Set expander ClickListener
    holder.card.setOnClickListener(v -> {
        if (position == expandedItemIndex) {
            holder.expanded = false;
            expandedItemIndex = -1;
        } else {
            holder.expanded = true;
            if (expandedItemIndex != -1) {
                BrewViewHolder otherHolder = ((BrewViewHolder) recyclerView.getChildViewHolder(
                        recyclerView.getChildAt(expandedItemIndex)));
                otherHolder.expanded = false;
            }
            expandedItemIndex = position;
        }
        notifyItemRangeChanged(0, recyclerView.getChildCount());
    });

}

【问题讨论】:

  • 最简单的方法是使用lisawray/GroupieExpandableGroups。
  • 希望在没有外部库的情况下做到这一点。他们的实现是否有简化?

标签: android android-recyclerview android-viewholder


【解决方案1】:

您不应该迭代所有视图。您只需保存展开视图的索引并调用notifyDataSetChangednotifyItemChanged

private int expandedItemIndex = -1;

@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder, 
               final int position, @NonNull Brew brew) {

    // Bind Views
    holder.bind(brew);

    // Set expander ClickListener
    holder.card.setOnClickListener(v -> {
        if (position == expandedItemIndex) {
            notifyItemChanged(position);
            expandedItemIndex = -1;
        } else {
            if (expandedItemIndex != -1) {
                notifyItemChanged(expandedItemIndex);
            }
            expandedItemIndex = position;
            notifyItemChanged(position);
        }
   });

   if (position == expandedItemIndex) {
      // Expand
      holder.quickActions.setVisibility(View.VISIBLE);
   } else {
      // Collapse
      holder.quickActions.setVisibility(View.GONE);
   }
}

【讨论】:

  • 这与我想要的很接近,但我认为如果将展开的项目滚动到屏幕外并单击另一个项目,它可能会导致 NPE?当您notifyItemChanged(positionOfItemOffScreen) 时会发生什么?此外,您会在项目实际更改之前通知...
  • @tobiasfried 我的代码没有得到 NPE。对于屏幕外项目方法 onBindViewHolder 将不会被调用。 notifyItemYYY 方法不会立即更新视图,所以一切正常。
【解决方案2】:

如果(vh!=null){ vh.expanded(false);}

或者

使用try,catch)))

【讨论】:

  • if (position == expandItemIndex) { // 展开 } else { // 折叠 }。我想它会给出相同的结果
  • 是的,仍然失败。我的问题是 mAdapter.getItemCount() 返回项目总数,无论是否膨胀。
  • 哈,好吧,尝试从适配器中获取最后一个可见元素,我在 onscrolllistener 中做到了,但我不确定,你怎么能在适配器中得到它
  • 哦,好的,我知道了,只要检查视图是否为空什么都不做
  • If(vh!=null){ vh.expanded(false);}
【解决方案3】:

通过迭代 i &lt; recyclerView.getChildCount() 并确保清理 onViewRecycled(ViewHolder holder) 中的 VH 来解决:

@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder, int position, @NonNull Brew brew) {
    // Bind Views
    holder.bind(brew);

    // Set expander ClickListener
    holder.card.setOnClickListener(v -> {
        for (int i = 0; i < recyclerView.getChildCount(); i++) {
            if (i != position) {
                BrewViewHolder otherHolder = ((BrewViewHolder) recyclerView.getChildViewHolder(recyclerView.getChildAt(i)));
                otherHolder.expanded = false;
            }
        }
        holder.expanded = !holder.expanded;
        notifyItemRangeChanged(0, recyclerView.getChildCount());
    });
}

@Override
public void onViewRecycled(@NonNull BrewViewHolder holder) {
    super.onViewRecycled(holder);
    holder.expanded = false;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多