【问题标题】:Expand/collapse a nested RecyclerView展开/折叠嵌套的 RecyclerView
【发布时间】:2019-04-13 16:35:02
【问题描述】:

我有一个带有多个 ViewHolder 的 RecyclerView 适配器。每个 ViewHolder 都有一个标题 TextView 和一个运行良好的嵌套 RecyclerView。但我想实现一个展开/折叠功能,以便在单击标题之前隐藏嵌套的 RecyclerView。我用这个方法RecyclerView expand/collapse items。它可以工作,但是当我单击标题以展开嵌套的 recyclerview 时,recyclerview 不会填充任何数据。需要明确的是,它检索数据但不可见。任何想法为什么会这样?

这是我的 onBindView 方法:

public class EligibilityAdapter extends RecyclerView.Adapter<EligibilityAdapter.ViewHolder> {

private Context mContext;
private List<EligibilityDetails> mEligsList;
private List<Items> mItemslist;
private LayoutInflater inflater;
private int mExpandedPosition = -1;

public EligibilityAdapter(Context context, List<EligibilityDetails> eligsList) {
    mContext = context;
    mEligsList = eligsList;
    inflater = LayoutInflater.from(context);
}

@Override
public int getItemViewType(int position) {
    switch (position) {
        case 0:
            return R.layout.rv_eligs_item_domestic;
        case 1:
            return R.layout.rv_eligs_item_overseas;
        default:
            return R.layout.rv_eligs_item_military;
    }
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    inflater = LayoutInflater.from(viewGroup.getContext());
    View view = inflater.inflate(i, viewGroup, false);
    ViewHolder holder = null;
    switch (i) {
        case R.layout.rv_eligs_item_domestic:
            holder = new DomesticViewHolder(view);
            break;
        case R.layout.rv_eligs_item_overseas:
            holder = new OverseasViewHolder(view);
            break;
        case R.layout.rv_eligs_item_military:
            holder = new MilitaryViewHolder(view);
            break;
    }
    return holder;
}

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

public abstract class ViewHolder extends RecyclerView.ViewHolder {
    RecyclerView itemsRv;
    TextView mHeader;
    ItemsAdapter adapter;

    public ViewHolder(View itemView) {
        super(itemView);
        mHeader = (TextView) itemView.findViewById(R.id.header_tv);
        itemsRv = itemView.findViewById(R.id.recyclerViewItems);
    }
    public void setData(List<Items> list) {
        adapter.updateList(list);
    }
    abstract void bind(EligibilityDetails item);
}

public class DomesticViewHolder extends ViewHolder {

    TextView mHeader;
    RecyclerView itemsRv;
    ItemsAdapter adapter;

    public DomesticViewHolder(View itemView) {
        super(itemView);
        mHeader = (TextView) itemView.findViewById(R.id.header_tv);
        itemsRv = itemView.findViewById(R.id.recyclerViewItems);
    }
    public void setData(List<Items> list) {
        adapter.updateList(list);
    }
    @Override
    void bind(EligibilityDetails eligibilityDetails) {
        mHeader.setText(eligibilityDetails.getRequirementHeader());
        mItemslist = eligibilityDetails.getItemsList();
        ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
        itemsRv.setHasFixedSize(true);
        itemsRv.setLayoutManager(new CustomLinearLayoutManager(mContext));
        itemsRv.setAdapter(itemsAdapter);
        itemsRv.setNestedScrollingEnabled(false);
    }
}

public class OverseasViewHolder extends ViewHolder {

    TextView mHeader;
    RecyclerView itemsRv;
    ItemsAdapter adapter;

    public OverseasViewHolder(View itemView) {
        super(itemView);
        mHeader = (TextView) itemView.findViewById(R.id.header_tv);
        itemsRv = itemView.findViewById(R.id.recyclerViewItems);
    }
    public void setData(List<Items> list) {
        adapter.updateList(list);
    }

    @Override
    void bind(EligibilityDetails eligibilityDetails) {
        mHeader.setText(eligibilityDetails.getRequirementHeader());
        mItemslist = eligibilityDetails.getItemsList();
        ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
        itemsRv.setHasFixedSize(true);
        itemsRv.setLayoutManager(new CustomLinearLayoutManager(mContext));
        itemsRv.setAdapter(itemsAdapter);
        itemsRv.setNestedScrollingEnabled(false);
    }
}

public class MilitaryViewHolder extends ViewHolder {

    TextView mHeader;
    RecyclerView itemsRv;
    ItemsAdapter adapter;

    public MilitaryViewHolder(View itemView) {
        super(itemView);
        mHeader = (TextView) itemView.findViewById(R.id.header_tv);
        itemsRv = itemView.findViewById(R.id.recyclerViewItems);
    }
    public void setData(List<Items> list) {
        adapter.updateList(list);
    }

    @Override
    void bind(EligibilityDetails eligibilityDetails) {
        mHeader.setText(eligibilityDetails.getRequirementHeader());
        mItemslist = eligibilityDetails.getItemsList();
        final ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
        itemsRv.setHasFixedSize(true);
        itemsRv.setLayoutManager(new CustomLinearLayoutManager(mContext));
        itemsRv.setAdapter(itemsAdapter);
        itemsRv.setNestedScrollingEnabled(false);
    }
}

@Override
public void onBindViewHolder(@NonNull final EligibilityAdapter.ViewHolder viewHolder, int i) {
    viewHolder.bind(mEligsList.get(i));

    final boolean isExpanded = i == mExpandedPosition;
    viewHolder.itemsRv.setVisibility(isExpanded?View.VISIBLE:View.GONE);
    viewHolder.itemView.setActivated(isExpanded);
    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mExpandedPosition = isExpanded ? -1:viewHolder.getAdapterPosition();
            ItemsAdapter itemsAdapter = new ItemsAdapter(mContext, mItemslist);
            viewHolder.itemsRv.setAdapter(itemsAdapter);
            //TransitionManager.beginDelayedTransition(recyclerView);
            notifyDataSetChanged();
        }
    });
}

其中 itemsRv 是嵌套的 RecyclerView。 我已经尝试将此逻辑移动到各个视图持有者并将 recyclerview 逻辑移动到此处,就像在 onClick 方法中设置适配器一样。每次都是空白。

提前致谢。

【问题讨论】:

  • 是否有另一个 RecyclerView 的原因,例如向另一个方向滚动?
  • 数据是从具有嵌套列表的 json 中获取的。嵌套列表有一个 CustomLinearLayoutManager 并且不滚动
  • 一般不是当前发生的情况 与标题和子项的唯一区别是项的外观吗?还是应该有不同的滚动方向?
  • 就是他们的样子。
  • 如果有帮助,我添加了整个适配器

标签: android android-recyclerview


【解决方案1】:

您可以创建一个多视图类型适配器,而不是为每个标题创建一个新的子Recyclerview,该适配器将为您的标题创建一个视图类型,为您的子项创建一个视图类型。

不要只为数据列表使用标题数据项,而是使用允许将每个数据类型转换为自己的视图类型的泛型类型。

为此,我们需要创建一个空接口,我们的所有数据类型都将实现它们都是通用的。

public interface GenericDataType {}

那么您的数据类型将如下所示

class HeaderItem implements GenericDataType {
    //All of your pojo data
    List<ChildrenItem> childrens; //ChildrenItem will also implement the GenericDataType that way both of the items are acceptable
}

所以我们所做的我们可以替换当前项目

private List<EligibilityDetails> mEligsList;
private List<Items> mItemslist;

private List<GenericDataType> adapterItems;

现在我们需要确保每当我们有某个ViewHolder 时,该位置的项目将是正确的类型。为此,我们需要更改我们的getItemViewType

@Override
public int getItemViewType(int position) {
    GenericItem currentItem = adapterItems.get(position);

    if (currentItem instanceOf HeaderItem) { //We have to use if else becasue java's switch does not supprt checking instancesOf
        return R.layout.your_header_item_layout_id;

    } else if(currentItem instanceOf ChildrenItem) {
        return R.layout.your_children_item_layout_id;

    } else if(repeat for any other types you have) {
        return R.layout.your_other_item_layout_id;

    } else {
        throw new Throwable("Unsupported type"); //This should never happen but we add it to make the compiler compile
    }
}

现在我们终于可以更改 onBind 方法来支持这两种类型了

@Override
public void onBindViewHolder(@NonNull final  EligibilityAdapter.ViewHolder viewHolder, int i) {

    if (viewHolder instanceOf HeaderViewHolder) {
        HeaderViewHolder holder = (HeaderViewHolder) viewHolder;
        headerItem = (HeaderItem) items.get(i);
        //Do rest of binding here

        holder.viewToAddMoreItems.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!headerItem.isEpanded()) { //Add to your header type a Boolean to check if it is expanded or not 
                    adapterItems.addAll(headerItem.getChildrens()); //They are the same generic type but the getItemViewType will handle it for us
                    notifyItemRangeInserted(); //Provied item start index and size of the list
                } else {
                    adapterItems.removeAll(headerItem.getChildrens());
                    notifyItemRangeRemoved(); I don't remember what it require but you'll figure that out
                }
                headerItem.setExpanded(!headerItem.isExpanded()); //Flipping the value 
            }
        });

    } else if (viewHolder instanceOf ChildrenViewHolder) {
            ChildrenViewHolder holder = (ChildrenViewHolder) viewHolder;
            childrenItem = (ChildrenItem) items.get(i);
            //Do your binding here

    } else if(repeate for other view-types) {}
}

【讨论】:

  • 感谢您抽出宝贵时间。我之前尝试过做类似的事情,但无法使其与嵌套的 recyclerview 兼容。您能否编辑我在上面发布的适配器文件以包含您的想法?
  • 具体来说,什么是HeaderViewHolder?它应该是 HeaderItem 吗?
  • 例如(它不需要匹配但就是这样),在你的情况下,HeaderViewHolder 将等于 DomesticViewHolder 和 ChildrenViewHolder 是 OverseasViewHolder
  • 如果我的回答对您有帮助,请批准并投票
  • 哦,好吧,没问题,如果你想看一个例子,我做了一个在编译时生成RecyclerView适配器代码的库,它的原理相同,有一个代码显示了什么生成,它应该告诉你你需要如何实现它,但它在 kotlin github.com/gilgoldzweig/Gencycler
猜你喜欢
  • 2019-03-07
  • 1970-01-01
  • 2015-01-28
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
  • 2019-03-21
  • 2012-07-19
  • 1970-01-01
相关资源
最近更新 更多