web-dev-qa-db-fra.com

Comment avoir un ListView / RecyclerView dans un parent RecyclerView?

Je souhaite ajouter des vues enfant (éléments de liste) qui me proviennent de données au format JSON. Chaque liste d'enfants se trouve sous une ligne d'élément de liste parent. Comment puis-je le renseigner dans RecyclerView pour chaque élément de ligne (éléments parents avec des éléments de liste enfant)?

J'ai essayé d'utiliser RecyclerView dans RecyclerView ligne parent (pour remplir des listes enfants), mais dans ce cas, les vues enfant ne sont pas visibles.

Classe d'adaptateur parent

public class DigitizedPrescAdapter extends RecyclerView.Adapter<DigitizedPrescAdapter.ListItemViewHolder>{
    private List<PrescriptionModal> prescriptionList;

    MedicinesInPrescAdapter adapter;

    public DigitizedPrescAdapter(List<PrescriptionModal> prescriptionListModal) {

        if (prescriptionListModal == null) {
            throw new IllegalArgumentException(
                    "PrescriptionList must not be null");
        }
        this.prescriptionList = prescriptionListModal;
    }

    @Override
    public ListItemViewHolder onCreateViewHolder(
            ViewGroup viewGroup, int viewType) {
        View itemView = LayoutInflater.
                from(viewGroup.getContext()).
                inflate(R.layout.item_row_digitised_request,
                        viewGroup,
                        false);
        return new ListItemViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(
            ListItemViewHolder viewHolder, int position) {
        PrescriptionModal model = prescriptionList.get(position);

        viewHolder.prescnum.setText("Prescription "+ ++position);
        viewHolder.prescNo.setText("Prescription: "+model.getPrescriptionID());
        viewHolder.doctorType.setText("Type: "+model.getDoctorType());
        viewHolder.doctorName.setText("Doctor: "+model.getDoctorName());
        viewHolder.patientName.setText("Patient: "+model.getPatientName());

        adapter = new MedicinesInPrescAdapter(model.getLstproduct());
        viewHolder.lstMedicines.setAdapter(adapter);

    }

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

    public final static class ListItemViewHolder
            extends RecyclerView.ViewHolder {

        TextView prescnum;
        TextView prescNo;
        TextView doctorType;
        TextView patientName;
        TextView doctorName;
        CheckBox selectAll;
        RecyclerView lstMedicines;

        public ListItemViewHolder(View itemView) {
            super(itemView);
            prescnum = (TextView) itemView.findViewById(R.id.prescnum);
            prescNo = (TextView) itemView.findViewById(R.id.prescNo);
            doctorType = (TextView) itemView.findViewById(R.id.doctorType);
            patientName = (TextView) itemView.findViewById(R.id.patientName);
            doctorName = (TextView) itemView.findViewById(R.id.doctorName);
            selectAll = (CheckBox) itemView.findViewById(R.id.selectAll);
            lstMedicines = (RecyclerView) itemView.findViewById(R.id.lstAllMedicines);
            MyLinearLayoutManager layoutManager = new MyLinearLayoutManager(itemView.getContext(),LinearLayoutManager.VERTICAL,false);
            lstMedicines.setHasFixedSize(false);
            lstMedicines.setLayoutManager(layoutManager);
        }
    }
}

Classe Adaptateur Enfant

public class MedicinesInPrescAdapter extends RecyclerView.Adapter<MedicinesInPrescAdapter.MedicineListItemViewHolder>{

    List<Modal_Product_List> prescriptionProducts;

    public MedicinesInPrescAdapter(List<Modal_Product_List> prescriptionListProd) {

        if (prescriptionListProd == null) {
            throw new IllegalArgumentException(
                    "PrescriptionProductList must not be null");
        }
        this.prescriptionProducts = prescriptionListProd;
    }

    @Override
    public MedicineListItemViewHolder onCreateViewHolder(
            ViewGroup viewGroup, int viewType) {
        View itemView = LayoutInflater.
                from(viewGroup.getContext()).
                inflate(R.layout.item_row_medicine_productlist,
                        viewGroup,
                        false);
        return new MedicineListItemViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(
            MedicineListItemViewHolder viewHolder, int position) {
        Modal_Product_List modelMedicine = prescriptionProducts.get(position);

        viewHolder.medicineName.setText(modelMedicine.getMedicinename());
        viewHolder.medQty.setText(modelMedicine.getQuantity());
        viewHolder.days.setText("30");
        viewHolder.Amount.setText(modelMedicine.getQuantitybasedprice());
    }

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

    public final static class MedicineListItemViewHolder
            extends RecyclerView.ViewHolder {

        TextView medicineName;
        EditText medQty;
        TextView days;
        TextView Amount;
        CheckBox selectMe;

        public MedicineListItemViewHolder(View itemView) {
            super(itemView);
            medicineName = (TextView) itemView.findViewById(R.id.medicineName);
            medQty = (EditText) itemView.findViewById(R.id.medQty);
            days = (TextView) itemView.findViewById(R.id.days);
            Amount = (TextView) itemView.findViewById(R.id.amount);
            selectMe = (CheckBox) itemView.findViewById(R.id.selectMe);
        }
    }
}
55
Ravi Kabra

J'ai eu ce problème il y a quelques jours et l'ai finalement résolu. Tout ce que vous avez à faire est @ remplacez le gestionnaire de disposition onMeasure comme ci-dessous:

CustomLinearLayoutManager

public class CustomLinearLayoutManager extends LinearLayoutManager {

    private static final String TAG = CustomLinearLayoutManager.class.getSimpleName();

    public CustomLinearLayoutManager(Context context) {
        super(context);
    }

    public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    private int[] mMeasuredDimension = new int[2];

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {

        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);
        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        int width = 0;
        int height = 0;
        for (int i = 0; i < getItemCount(); i++) {
            measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                mMeasuredDimension);


            if (getOrientation() == HORIZONTAL) {
                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }
        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        setMeasuredDimension(width, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                               int heightSpec, int[] measuredDimension) {
        try {
            View view = recycler.getViewForPosition(0);//fix IndexOutOfBoundsException

            if (view != null) {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();

                int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight(), p.width);

                int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);

                view.measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
                measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
                recycler.recycleView(view);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

Vous pouvez simplement copier et coller le gestionnaire de disposition personnalisé comme ci-dessus et configurer votre vue de recyclage dans l'adaptateur parent de la manière suivante:

RecyclerView.LayoutManager layoutManager = new CustomLinearLayoutManager(mContext);
holder.childRecyclerView.setLayoutManager(layoutManager);

Rappelez-vous: N'utilisez pas le même layoutManager comme adaptateur parent, sinon une erreur se produira.

57
pptang

Si je comprends bien, vous voulez un RecyclerView avec des lignes RecyclerView. Dans ce cas, je vous recommande d'utiliser un extensible RecyclerView , en utilisant this lib . Il suffit de suivre le exemple extensible dans le lien.

Il y a beaucoup d'autres fonctionnalités intéressantes dans la même bibliothèque, comme le glisser-déposer, les rangées balayables ... Regardez ceci moins d'une minute exemple de vidéo .

Vous devez juste ajouter la bibliothèque aux dépendances dans votre gradle.build fichier, comme suit:

dependencies {
    compile 'com.h6ah4i.Android.widget.advrecyclerview:advrecyclerview:0.7.4'
}

Pour pouvoir import la lib dans vos fichiers Java.

14
Geraldo Neto

Par Android Support Library 23.2 d’une version de la bibliothèque d’assistance 23.2.0. Donc, tout WRAP_CONTENT Devrait fonctionner correctement.

Veuillez mettre à jour la version d'une bibliothèque dans le fichier gradle.

compile 'com.Android.support:recyclerview-v7:23.2.0'

Cela permet à un RecyclerView de se redimensionner lui-même en fonction de la taille de son contenu. Cela signifie que des scénarios précédemment indisponibles, tels que l'utilisation de WRAP_CONTENT Pour une dimension de RecyclerView, sont désormais possibles. Vous constaterez que tous les gestionnaires de mises en forme intégrés prennent désormais en charge la mesure automatique.

vous devrez appeler setAutoMeasureEnabled(true)

Ci-dessous l'exemple de code

RecyclerView.LayoutManager layout = new LinearLayoutManager(context); 
layout.setAutoMeasureEnabled(true);

As setAutoMeasureEnabled est obsolète. Solution alternative Cette méthode est obsolète en API niveau 27.1.0. Les développeurs de LayoutManager doivent définir s'ils utilisent ou non AutoMeasure en remplaçant isAutoMeasureEnabled().

14
Maheshwar Ligade

Remarque: vous ne devez pas créer de nouvel adaptateur chaque fois que vous appelez onBindView (), vous devez le faire une fois dans onCreateView ().

La meilleure méthode - pour utiliser n'importe quelle bibliothèque, par exemple - RendererRecyclerViewAdapter

Comment ajouter un NestedRecyclerView:

Étape 1: Ajoutez l'interface ViewModel à votre Modal_Product_List

public class Modal_Product_List implements ViewModel {

    String getMedicinename() { ... } //your method
    int getQuantity() { ... } //your method
    int getQuantitybasedprice() { ... } //your method
}

Étape 2: Créez un ViewBinder pour le Modal_Product_List:

private ViewRenderer getModalProductViewRenderer() {
    return new ViewBinder<>(
            R.layout.item_row_medicine_productlist, //your child layout id
            Modal_Product_List.class //your child item class
            (model, Finder, payloads) -> Finder
                .setText(R.id.medicineName, model.getMedicinename())
                .setText(R.id.medQty, (String) model.getQuantity())
                .setText(R.id.amount, (String) model.getQuantitybasedprice())
                .setChecked(R.id.selectMe, ...)
    );
}

Étape 3: Ajoutez l'interface CompositeViewModel à PrescriptionModal ou étendez de DefaultCompositeModel:

public class PrescriptionModal extends DefaultCompositeViewModel {

    String getPrescriptionID() {...} //your method
    String getDoctorType() {...} //your method
    String getDoctorName() {...} //your method
    String getPatientName() {...} //your method

    @Override
    List<Modal_Product_List> getItems() { return yourProductItems; }
}

Étape 4: Créez un ViewBinder pour le PrescriptionModal:

private ViewRenderer getModalProductViewRenderer() {
    return new CompositeViewBinder<>(
        R.layout.item_row_digitised_request, //your parent item layout
        R.id.lstAllMedicines, //your nested RecyclerView
        PrescriptionModal.class, //your parent item class
        (model, Finder, payloads) -> Finder
            //no need to set child items, it will set automatically
            .setText(R.id.prescnum, "Prescription:" + model.getPrescriptionID)
            .setText(R.id.doctorName, "Doctor:" + model.getDoctorName())
            .setText(R.id.doctorType, "Type:" + model.getDoctorType())
            .setText(R.id.patientName, "Patient:" + model.getPatientName())
    ).registerRenderer(getModalProductViewRenderer()); //register ModalProductViewRenderer
     .registerRenderer(...) //if you need you can create other renderer and register here
);

Étape 5 (facultatif): Si vous avez besoin d'un Custom LayoutManager, étendez le CompositeViewBinder et remplacez le createLayoutManager méthode et l'utiliser à la place de CompositeViewBinder

public class CustomCompositeViewBinder extends CompositeViewBinder {

    //...

    @Override
    protected RecyclerView.LayoutManager createLayoutManager() {
        return new MyLinearLayoutManager(getContext(), VERTICAL, false);
    }
}

Étape 6: Initialisez RendererRecyclerViewAdapter et enregistrez le rendu:

RendererRecyclerViewAdapter adapter = new RendererRecyclerViewAdapter(getContext());
recyclerView.setAdapter(adapter);

adapter.registerRenderer(getModalProductViewRenderer());
adapter.registerRenderer(...); //if you need you can create other renderers

adapter.setItems(getPrescriptionListModal());

C'est un moyen très simple et rapide pour ajouter un Nested RecyclerView

0
Vitaly