【问题标题】:How to have a ListView/RecyclerView inside a parent RecyclerView?如何在父 RecyclerView 中有一个 ListView/RecyclerView?
【发布时间】:2015-08-14 14:03:18
【问题描述】:

我想添加一些来自 JSON 格式数据的子视图(列表项)。每个子列表都位于父列表项行下。如何为每个行项(具有子列表项的父项)在 RecyclerView 中填充它?

我已尝试在 RecyclerView 父行中使用 RecyclerView(用于填充子列表),但此处子视图不可见。

父适配器类

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

子适配器类

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

【问题讨论】:

标签: android android-recyclerview


【解决方案1】:

我几天前遇到了这个问题,终于解决了。您所要做的就是 @override 布局管理器 onMeasure 函数如下:

自定义线性布局管理器

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

您可以像上面一样复制并粘贴自定义布局管理器,然后在父适配器中设置您的回收器视图,如下所示:

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

切记:不要使用与父适配器相同的layoutManager,否则会出错。

【讨论】:

  • 感谢 pptang,它工作正常,但高度仍然是一个问题,如果我尝试滚动,一些孩子会失去可见性,而且我有一些行中的编辑文本。所以我需要他们的数据在滚动时不应该丢失..
  • 但是如果嵌套的 RecyclerView 有不可预测的元素数量怎么办?然后每次这个 RecyclerView 出现在它的父 RecyclerView 的行中,它就会有不同的高度。
  • 我知道你在说什么。在这种情况下它仍然有效。您在尝试时出现任何问题?
  • @pptang 我按照你的建议做了我在儿童回收站视图下方丢失了物品我该怎么办?
  • @VivekPratapSingh 将您的代码 sn-p 发送给我,我可以帮助修复它。我的邮箱:donaldduck518@gmail.com
【解决方案2】:

Android Support Library 23.2 支持库版本 23.2.0。所以所有WRAP_CONTENT 应该都能正常工作。

请更新 gradle 文件中的库版本。

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

这允许RecyclerView 根据其内容的大小调整自身大小。这意味着以前不可用的方案(例如将WRAP_CONTENT 用于RecyclerView 的维度)现在成为可能。您会发现所有内置的 LayoutManagers 现在都支持自动测量。

您需要致电setAutoMeasureEnabled(true)

下面是示例代码

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

由于 setAutoMeasureEnabled 已弃用替代解决方案 此方法在 API 级别 27.1.0 中已弃用。 LayoutManager 的实现者应该通过覆盖 isAutoMeasureEnabled() 来定义它是否使用 AutoMeasure。

【讨论】:

  • setAutoMeasureEnabled 应该调用什么?
  • 布局管理器。 RecyclerView.LayoutManager layout = new LinearLayoutManager(context); layout.setAutoMeasureEnabled(true);
  • 我看到 automeasureenabled 已被弃用。但是我们应该使用什么呢?我没有看到提到任何替代品
  • @j2emanue 根据您已覆盖 LayoutManager 并将 isAutoMesure 设置为 true 的文档
  • 此方法在 API 级别 27.1.0 中已弃用。 LayoutManager 的实现者应该通过覆盖 isAutoMeasureEnabled() 来定义它是否使用 AutoMeasure。
【解决方案3】:

如果我理解,您想要一个带有 RecyclerView 行的 RecyclerView。 在这种情况下,我建议您使用 可扩展的 RecyclerView,使用 this lib。只需点击链接中的expandable example

同一个库中还有更多很酷的功能,例如拖放、可滑动的行...观看此视频不到一分钟 example video

您只需将 lib 添加到 gradle.build 文件中的依赖项中,如下所示:

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

为了能够import你的java文件中的lib。

【讨论】:

  • 嘿 Geraldo 我正在尝试使用这个库,只是注意到它说:“如果您使用支持库 v23.0.x,请改用 v0.8.1。”在我的依赖项中,我有 compile 'com.android.support:recyclerview-v7:23.0.0'compile 'com.android.support:cardview-v7:23.0.0' compile 'com.android.support:appcompat-v7:23.0.0' 我必须更改它吗?
【解决方案4】:

请注意:你不应该每次调用onBindView()时都创建新的Adapter,你应该在onCreateView()中创建一次。

最佳方式 - 使用任何库,例如 - RendererRecyclerViewAdapter

如何添加 NestedRecyclerView:

第 1 步:ViewModel 接口添加到您的Modal_Product_List

public class Modal_Product_List implements ViewModel {

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

第 2 步:Modal_Product_List 创建一个ViewBinder

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, ...)
    );
}

第 3 步:CompositeViewModel 接口添加到PrescriptionModal 或从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; }
}

第 4 步:PrescriptionModal 创建一个ViewBinder

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

第 5 步(可选):如果您需要自定义 LayoutManager,则扩展 CompositeViewBinder 并覆盖 createLayoutManager 方法并使用它而不是 CompositeViewBinder

public class CustomCompositeViewBinder extends CompositeViewBinder {

    //...

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

第六步:初始化RendererRecyclerViewAdapter并注册渲染器:

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

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

adapter.setItems(getPrescriptionListModal());

添加嵌套的 RecyclerView 真的很短很简单

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-26
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多