¿Cómo tener un ListView / RecyclerView dentro de un RecyclerView padre?

Quiero agregar algunas vistas secundarias (elementos de lista), que me llegan de datos formateados JSON. Cada lista de elementos secundarios se encuentra debajo de una fila de elementos de la lista principal. ¿Cómo puedo RecyclerView en RecyclerView para cada elemento de la fila (elementos principales con elementos de la lista infantil)?

He intentado usar RecyclerView dentro de la fila principal de RecyclerView (para llenar listas secundarias), pero aquí las vistas secundarias no están visibles.

Clase de adaptador principal

 public class DigitizedPrescAdapter extends RecyclerView.Adapter{ private List prescriptionList; MedicinesInPrescAdapter adapter; public DigitizedPrescAdapter(List 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); } } } 

Clase de adaptador para niños

 public class MedicinesInPrescAdapter extends RecyclerView.Adapter{ List prescriptionProducts; public MedicinesInPrescAdapter(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); } } } 

Recibí este problema hace unos días y finalmente lo resolví. Todo lo que tiene que hacer es @override la función de gestor de disposición onMeasure de la siguiente manera:

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

Puede copiar y pasar el administrador de diseño personalizado como se indica anteriormente y configurar su vista de reciclador en el adaptador principal de esta manera:

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

Recuerde : no use el mismo layoutManager como adaptador principal, o se producirá un error.

Por Android Support Library 23.2 de una biblioteca de soporte versión 23.2.0. Entonces, todo WRAP_CONTENT debería funcionar correctamente.

Actualice la versión de una biblioteca en un archivo gradle .

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

Esto permite que un RecyclerView clasifique en función del tamaño de su contenido. Esto significa que los escenarios anteriormente no disponibles, como el uso de WRAP_CONTENT para una dimensión de RecyclerView , ahora son posibles. Descubrirá que todos los administradores de diseño integrados ahora son compatibles con la medición automática.

se le setAutoMeasureEnabled(true) llame a setAutoMeasureEnabled(true)

A continuación se muestra el código de muestra

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

Como setAutoMeasureEnabled está en desuso, la solución alternativa Este método ya no está disponible en el nivel API 27.1.0. Los implementadores de LayoutManager deben definir si utilizan o no AutoMeasure anulando isAutoMeasureEnabled() .

Si lo entiendo, quieres un RecyclerView con filas RecyclerView. En este caso, le recomiendo que use un RecyclerView expandible , utilizando esta lib . Solo sigue el ejemplo expandible dentro del enlace.

Hay muchas más características geniales en la misma lib, como Arrastrar y soltar, Filas que se pueden deslizar … Mire este video de ejemplo de menos de un minuto.

Solo tiene que agregar la lib a las dependencias en su archivo gradle.build , como por ejemplo:

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

Para poder import la lib en sus archivos java.

Tenga en cuenta que no debe crear un nuevo Adapter cada vez que llame aBindView (), debe hacerlo una vez en onCreateView ().

La mejor manera: usar cualquier biblioteca, por ejemplo, RendererRecyclerViewAdapter

Cómo agregar un NestedRecyclerView:

Paso 1: agregue la interfaz ViewModel a su Modal_Product_List

 public class Modal_Product_List implements ViewModel { String getMedicinename() { ... } //your method int getQuantity() { ... } //your method int getQuantitybasedprice() { ... } //your method } 

Paso 2: Crea un ViewBinder para 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, ...) ); } 

Paso 3: Agregue la interfaz CompositeViewModel a PrescriptionModal o DefaultCompositeModel desde DefaultCompositeModel :

 public class PrescriptionModal extends DefaultCompositeViewModel { String getPrescriptionID() {...} //your method String getDoctorType() {...} //your method String getDoctorName() {...} //your method String getPatientName() {...} //your method @Override List getItems() { return yourProductItems; } } 

Paso 4: Crea un ViewBinder para el 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 ); 

Paso 5 (opcional): si necesita un LayoutManager personalizado, extienda CompositeViewBinder y anule el método createLayoutManager y createLayoutManager en lugar de CompositeViewBinder

 public class CustomCompositeViewBinder extends CompositeViewBinder { //... @Override protected RecyclerView.LayoutManager createLayoutManager() { return new MyLinearLayoutManager(getContext(), VERTICAL, false); } } 

Paso 6: Inicialice RendererRecyclerViewAdapter y registre el renderizador:

 RendererRecyclerViewAdapter adapter = new RendererRecyclerViewAdapter(getContext()); recyclerView.setAdapter(adapter); adapter.registerRenderer(getModalProductViewRenderer()); adapter.registerRenderer(...); //if you need you can create other renderers adapter.setItems(getPrescriptionListModal()); 

Es una manera muy corta y simple de agregar un Reciclador nested.