Mam adapter RecyclerView z wieloma ViewHolders. Każdy ViewHolder ma nagłówek TextView i zagnieżdżony RecyclerView, który działał poprawnie. Ale chciałem zaimplementować funkcję expand/collapse, aby zagnieżdżony RecyclerView był ukryty do momentu kliknięcia nagłówka. Użyłem tej metody RecyclerView expand/collapse items. Działa, ale kiedy klikam nagłówek, aby rozwinąć zagnieżdżony widok recyklera, widok recyclerview nie wypełnia żadnych danych. Żeby było jasne, pobiera dane, ale nie są widoczne. Jakieś pomysły, dlaczego tak się dzieje?

To jest moja metoda onBindViewMethod:

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

Gdzie itemsRv jest zagnieżdżonym RecyclerView. Próbowałem przenieść tę logikę do poszczególnych elementów widoku i przenieść logikę widoku recyclerview tutaj, tak jak ustawianie adaptera wewnątrz metody onClick. Za każdym razem jest puste.

Z góry dziękuję.

0
Emily 10 listopad 2018, 10:50
Czy istnieje powód do innego RecyclerView, na przykład przewijania w innym kierunku?
 – 
Gil Goldzweig
10 listopad 2018, 12:31
Dane są pobierane z json, który miał zagnieżdżone listy. Zagnieżdżona lista ma CustomLinearLayoutManager i nie jest przewijana
 – 
Emily
10 listopad 2018, 12:34
Czy ogólnie nie jest to, co się obecnie dzieje. Czy jedyną różnicą w stosunku do nagłówka i elementu podrzędnego jest wygląd elementu? A może powinien być też inny kierunek przewijania?
 – 
Gil Goldzweig
10 listopad 2018, 12:36
Po prostu tak, jak wyglądają.
 – 
Emily
10 listopad 2018, 12:37
Dodałem cały adapter jeśli jest pomocny
 – 
Emily
10 listopad 2018, 12:44

1 odpowiedź

Najlepsza odpowiedź

Zamiast mieć nowy sub Recyclerview dla każdego nagłówka, możesz utworzyć adapter z wieloma widokami, który będzie miał typ widoku dla nagłówka i typ widoku dla elementu podrzędnego.

Zamiast używać tylko elementu danych nagłówka dla listy danych, użyj typu ogólnego, który umożliwi rzutowanie każdego typu danych na jego własny typ widoku.

Aby to zrobić, musimy stworzyć pusty interfejs, w którym wszystkie nasze typy danych będą zaimplementowane w ten sposób, że będą generyczne.

public interface GenericDataType {}

Więc wtedy twój typ danych będzie wyglądał tak

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
}

A więc takie, które zrobiliśmy, że możemy wymienić obecne elementy z

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

Do

private List<GenericDataType> adapterItems;

Teraz musimy się upewnić, że za każdym razem, gdy mamy określony ViewHolder przedmiot na tej pozycji będzie odpowiedniego typu. W tym celu musimy zmienić nasze 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
    }
}

Teraz możemy wreszcie zmienić naszą metodę onBind, aby obsługiwała oba typy

@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) {}
}
1
Gil Goldzweig 10 listopad 2018, 13:39
Dzięki za poświęcenie czasu. Próbowałem zrobić coś podobnego wcześniej i nie mogłem uczynić go kompatybilnym z zagnieżdżonym widokiem recyklera. Czy możesz edytować plik adaptera, który opublikowałem powyżej, aby uwzględnić swój pomysł?
 – 
Emily
10 listopad 2018, 14:35
W szczególności, czym jest HeaderViewHolder? Czy to ma być HeaderItem?
 – 
Emily
10 listopad 2018, 14:37
Na przykład (nie musi się zgadzać, ale to jest pomysł), w twoim przypadku HeaderViewHolder będzie równy DomesticViewHolder, a ChildrenViewHolder będzie OverseasViewHolder
 – 
Gil Goldzweig
10 listopad 2018, 14:39
Jeśli moja odpowiedź Ci pomogła, zatwierdź ją i zagłosuj
 – 
Gil Goldzweig
10 listopad 2018, 16:42
1
Aha ok, nie ma problemu, jeśli chcesz zobaczyć przykład, zrobiłem bibliotekę, która generuje kod adaptera RecyclerView w czasie kompilacji, działa na tej samej zasadzie, jest pokazany kod, który został wygenerowany i powinien pokazać, jak trzeba to zaimplementować, ale jest w kotlin github.com/gilgoldzweig/Gencycler
 – 
Gil Goldzweig
10 listopad 2018, 16:52