web-dev-qa-db-fra.com

Comment ajouter un recyclerView dans un autre recyclerView

Je prévois de développer une application qui affiche des données dynamiques dans un recyclerCardView. J'ai donc décidé d'ajouter un recyclerView appelé CheckBoxRecyclerView à l'intérieur de mon principal recyclerView. Ceci est mon code pour mon application:

Mon activité principale:

setContentView(R.layout.activity_startup);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
reminderView = (RecyclerView) findViewById(R.id.reminder_recycler_view);
RlayoutManager = new LinearLayoutManager(this);
reminderView.setLayoutManager(RlayoutManager);

setSupportActionBar(toolbar);
cardView = (CardView) findViewById(R.id.card_first);
cardView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(getApplicationContext() , ReminderActivity.class);
        startActivity(intent);
    }
});
ReminderHelper helper = new ReminderHelper(getApplicationContext());
ReminderAdapter reminderAdapter = new ReminderAdapter(helper);
ContentValues reminderValues = new ContentValues();
ContentValues checkboxValues = new ContentValues();
// Devlopment Part ->
reminderValues.put("reminderTitle" , "A Reminder Title");
reminderValues.put("reminderLastModDate" , 0);
reminderValues.put("reminderAlarm" , 0);
reminderValues.put("reminderPicURI" , "skjshksjh");
reminderValues.put("ReminderBackground" , "#00796b");
checkboxValues.put("checkboxText" , "This is a CheckBox");
checkboxValues.put("isDone" , false);
checkboxValues.put("checkboxReminderID" , 0);
reminderAdapter.INSERT_REMINDER(reminderValues);
reminderAdapter.INSERT_CHECKBOX(checkboxValues);
File dbPath = getApplicationContext().getDatabasePath(ReminderHelper.DATABASE_NAME);
if(dbPath.exists()){
    List<Reminder> reminders = new ReminderAdapter(helper).getAllReminders();
    List<CheckBoxItem> checkBoxItems = new ReminderAdapter(helper).getAllCheckBoxes();
    RAdapter = new RAdapter(reminders , getApplicationContext() , checkBoxItems);
    reminderView.setAdapter(RAdapter);
}else{

}

Et c'est le fichier de mise en page:

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical"
    Android:paddingBottom="8dp"
    Android:paddingLeft="8dp"
    Android:paddingRight="8dp"
    Android:paddingTop="8dp"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.smflog.sreminder.StartupActivity"
    tools:showIn="@layout/app_bar_startup">

    <Android.support.v7.widget.RecyclerView
        Android:layout_width="match_parent"
        Android:id="@+id/reminder_recycler_view"
        Android:scrollbars="vertical"
        Android:layout_height="match_parent">

et à l'intérieur de cette recyclerView il y a un autre:

<Android.support.v7.widget.CardView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:card_view="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:id="@+id/reminder_card"
    card_view:cardCornerRadius="2dp"
    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true">

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:orientation="vertical"
        Android:paddingBottom="16dp"
        Android:paddingLeft="8dp">

        <com.smflog.sreminder.utils.TitleView
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:id="@+id/reminder_title"
            Android:paddingTop="8dp"
            Android:text="Wellcome To Google Keep !"
            Android:textSize="15dp"
            Android:textStyle="bold" />
        <LinearLayout
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content" Android:orientation="horizontal">
<Android.support.v7.widget.RecyclerView
    Android:layout_width="wrap_content"
    Android:id="@+id/checkbox_recycler_view"
    Android:layout_height="wrap_content">

</Android.support.v7.widget.RecyclerView>
        </LinearLayout>

    </LinearLayout>
</Android.support.v7.widget.CardView>

Leurs adaptateurs, Main (RAdapter):

public class RAdapter extends RecyclerView.Adapter<RAdapter.ViewHolder> {
    List<Reminder> reminder;
    private Context context;
    private LinearLayoutManager lln;
    private CAdapter checkBoxAdapter;
    private List<CheckBoxItem> checkBoxItems;
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public CardView rCardView;
        public RecyclerView recyclerView;
        public TitleView rTitleView;
        public ViewHolder(View itemView) {
            super(itemView);
            rCardView = (CardView) itemView.findViewById(R.id.reminder_card);
            rTitleView = (TitleView) itemView.findViewById(R.id.reminder_title);
            recyclerView = (RecyclerView) itemView.findViewById(R.id.checkbox_recycler_view);
        }
    }

    public RAdapter(List<Reminder> reminder, Context context, List<CheckBoxItem> checkBoxItems) {
        this.reminder = reminder;
        this.context = context;
        this.checkBoxItems = checkBoxItems;
    }

    @Override
    public RAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.reminder_card, parent, false);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(RAdapter.ViewHolder holder, int position) {
        lln = new LinearLayoutManager(context);
        holder.recyclerView.setLayoutManager(lln);
        checkBoxAdapter = new CAdapter(checkBoxItems, context);
        holder.recyclerView.setAdapter(checkBoxAdapter);
        holder.rCardView.setCardBackgroundColor(Color.parseColor("#00796b"));
        holder.rTitleView.setText(reminder.get(position).getReminderTitle());
    }

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

Et deuxième adaptateur:

public class CAdapter extends RecyclerView.Adapter<CAdapter.ViewHolder> {
    List<CheckBoxItem> checkBoxItems;
    Context context;

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TitleView checkBoxTitle;
        public ImageView deleteCheckBox;
        public CheckBox checkBoxCheckBox;

        public ViewHolder(View itemView) {
            super(itemView);
            checkBoxTitle = (TitleView) itemView.findViewById(R.id.checkbox_item_text);
            checkBoxCheckBox = (CheckBox) itemView.findViewById(R.id.checkbox_item_checkbox);
            Log.d("CAdapterLog", "Adpater Holded !!!!! :( ");
            deleteCheckBox = (ImageView) itemView.findViewById(R.id.btn_delete_checkbox);
        }
    }

    public CAdapter(List<CheckBoxItem> checkBoxItems, Context context) {
        this.checkBoxItems = checkBoxItems;
        this.context = context;
        Log.d("CAdapterLog", "Adpater Created !!!!! :( ");
    }


    @Override
    public CAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.checkbox_item, parent, false);
        ViewHolder vh = new ViewHolder(v);
        Log.d("CAdapterLog", "Adpater ViewHolded :( !!!!! :( ");
        return vh;
    }

    @Override
    public void onBindViewHolder(CAdapter.ViewHolder holder, int position) {
        Boolean isCheckboxChecked = Boolean.parseBoolean(checkBoxItems.get(position).getCheckBoxIsDone());
        String checkBoxText = checkBoxItems.get(position).getCheckBoxBody();
        Log.d("CAdapterLog", "Adpater Binded :( ");
        final int checkboxID = Integer.parseInt(checkBoxItems.get(position).getCheckBoxID());
        int reminderCheckBoxID = Integer.parseInt(checkBoxItems.get(position).getCheckBoxReminderID());
        holder.deleteCheckBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("CAdapterLog", "Cross Button Clicked !");
            }
        });
        holder.checkBoxCheckBox.setChecked(isCheckboxChecked);
        holder.checkBoxTitle.setText(checkBoxText);
    }

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

}

Et mon problème: comme vous le voyez dans CAdapter, seul le message du constructeur Log affiché.

[~ # ~] update [~ # ~] : s'il existe un autre moyen d'afficher des données dynamiques dans une autre carte dynamique, si oui vaut-il mieux l'utiliser au lieu de recyclerView?
quelqu'un m'aide?
La sortie: sortie de l'application comme vous voyez, le setTitle for RAdapter fonctionne.

43
Mohamamd Fatemi

J'aimerais suggérer d'utiliser un seul RecyclerView et de renseigner les éléments de votre liste de manière dynamique. J'ai ajouté un projet github pour décrire comment cela peut être fait. Vous pourriez avoir un coup d'oeil. Bien que les autres solutions fonctionnent très bien, je voudrais suggérer qu’il s’agit d’un moyen beaucoup plus rapide et efficace d’afficher plusieurs listes dans un RecyclerView.

L'idée est d'ajouter une logique dans votre méthode onCreateViewHolder et onBindViewHolder afin que vous puissiez gonfler la vue appropriée pour les positions exactes dans votre RecyclerView.

J'ai ajouté un exemple de projet avec ce wiki également. Vous pouvez cloner et vérifier ce qu'il fait. Pour plus de commodité, je publie l'adaptateur que j'ai utilisé.

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

    private static final int FOOTER_VIEW = 1;
    private static final int FIRST_LIST_ITEM_VIEW = 2;
    private static final int FIRST_LIST_HEADER_VIEW = 3;
    private static final int SECOND_LIST_ITEM_VIEW = 4;
    private static final int SECOND_LIST_HEADER_VIEW = 5;

    private ArrayList<ListObject> firstList = new ArrayList<ListObject>();
    private ArrayList<ListObject> secondList = new ArrayList<ListObject>();

    public DynamicListAdapter() {
    }

    public void setFirstList(ArrayList<ListObject> firstList) {
        this.firstList = firstList;
    }

    public void setSecondList(ArrayList<ListObject> secondList) {
        this.secondList = secondList;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        // List items of first list
        private TextView mTextDescription1;
        private TextView mListItemTitle1;

        // List items of second list
        private TextView mTextDescription2;
        private TextView mListItemTitle2;

        // Element of footer view
        private TextView footerTextView;

        public ViewHolder(final View itemView) {
            super(itemView);

            // Get the view of the elements of first list
            mTextDescription1 = (TextView) itemView.findViewById(R.id.description1);
            mListItemTitle1 = (TextView) itemView.findViewById(R.id.title1);

            // Get the view of the elements of second list
            mTextDescription2 = (TextView) itemView.findViewById(R.id.description2);
            mListItemTitle2 = (TextView) itemView.findViewById(R.id.title2);

            // Get the view of the footer elements
            footerTextView = (TextView) itemView.findViewById(R.id.footer);
        }

        public void bindViewSecondList(int pos) {

            if (firstList == null) pos = pos - 1;
            else {
                if (firstList.size() == 0) pos = pos - 1;
                else pos = pos - firstList.size() - 2;
            }

            final String description = secondList.get(pos).getDescription();
            final String title = secondList.get(pos).getTitle();

            mTextDescription2.setText(description);
            mListItemTitle2.setText(title);
        }

        public void bindViewFirstList(int pos) {

            // Decrease pos by 1 as there is a header view now.
            pos = pos - 1;

            final String description = firstList.get(pos).getDescription();
            final String title = firstList.get(pos).getTitle();

            mTextDescription1.setText(description);
            mListItemTitle1.setText(title);
        }

        public void bindViewFooter(int pos) {
            footerTextView.setText("This is footer");
        }
    }

    public class FooterViewHolder extends ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
        }
    }

    private class FirstListHeaderViewHolder extends ViewHolder {
        public FirstListHeaderViewHolder(View itemView) {
            super(itemView);
        }
    }

    private class FirstListItemViewHolder extends ViewHolder {
        public FirstListItemViewHolder(View itemView) {
            super(itemView);
        }
    }

    private class SecondListHeaderViewHolder extends ViewHolder {
        public SecondListHeaderViewHolder(View itemView) {
            super(itemView);
        }
    }

    private class SecondListItemViewHolder extends ViewHolder {
        public SecondListItemViewHolder(View itemView) {
            super(itemView);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v;

        if (viewType == FOOTER_VIEW) {
            v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent, false);
            FooterViewHolder vh = new FooterViewHolder(v);
            return vh;

        } else if (viewType == FIRST_LIST_ITEM_VIEW) {
            v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_first_list, parent, false);
            FirstListItemViewHolder vh = new FirstListItemViewHolder(v);
            return vh;

        } else if (viewType == FIRST_LIST_HEADER_VIEW) {
            v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_first_list_header, parent, false);
            FirstListHeaderViewHolder vh = new FirstListHeaderViewHolder(v);
            return vh;

        } else if (viewType == SECOND_LIST_HEADER_VIEW) {
            v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_second_list_header, parent, false);
            SecondListHeaderViewHolder vh = new SecondListHeaderViewHolder(v);
            return vh;

        } else {
            // SECOND_LIST_ITEM_VIEW
            v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_second_list, parent, false);
            SecondListItemViewHolder vh = new SecondListItemViewHolder(v);
            return vh;
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        try {
            if (holder instanceof SecondListItemViewHolder) {
                SecondListItemViewHolder vh = (SecondListItemViewHolder) holder;
                vh.bindViewSecondList(position);

            } else if (holder instanceof FirstListHeaderViewHolder) {
                FirstListHeaderViewHolder vh = (FirstListHeaderViewHolder) holder;

            } else if (holder instanceof FirstListItemViewHolder) {
                FirstListItemViewHolder vh = (FirstListItemViewHolder) holder;
                vh.bindViewFirstList(position);

            } else if (holder instanceof SecondListHeaderViewHolder) {
                SecondListHeaderViewHolder vh = (SecondListHeaderViewHolder) holder;

            } else if (holder instanceof FooterViewHolder) {
                FooterViewHolder vh = (FooterViewHolder) holder;
                vh.bindViewFooter(position);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public int getItemCount() {

        int firstListSize = 0;
        int secondListSize = 0;

        if (secondList == null && firstList == null) return 0;

        if (secondList != null)
            secondListSize = secondList.size();
        if (firstList != null)
            firstListSize = firstList.size();

        if (secondListSize > 0 && firstListSize > 0)
            return 1 + firstListSize + 1 + secondListSize + 1;   // first list header, first list size, second list header , second list size, footer
        else if (secondListSize > 0 && firstListSize == 0)
            return 1 + secondListSize + 1;                       // second list header, second list size, footer
        else if (secondListSize == 0 && firstListSize > 0)
            return 1 + firstListSize;                            // first list header , first list size
        else return 0;
    }

    @Override
    public int getItemViewType(int position) {

        int firstListSize = 0;
        int secondListSize = 0;

        if (secondList == null && firstList == null)
            return super.getItemViewType(position);

        if (secondList != null)
            secondListSize = secondList.size();
        if (firstList != null)
            firstListSize = firstList.size();

        if (secondListSize > 0 && firstListSize > 0) {
            if (position == 0) return FIRST_LIST_HEADER_VIEW;
            else if (position == firstListSize + 1)
                return SECOND_LIST_HEADER_VIEW;
            else if (position == secondListSize + 1 + firstListSize + 1)
                return FOOTER_VIEW;
            else if (position > firstListSize + 1)
                return SECOND_LIST_ITEM_VIEW;
            else return FIRST_LIST_ITEM_VIEW;

        } else if (secondListSize > 0 && firstListSize == 0) {
            if (position == 0) return SECOND_LIST_HEADER_VIEW;
            else if (position == secondListSize + 1) return FOOTER_VIEW;
            else return SECOND_LIST_ITEM_VIEW;

        } else if (secondListSize == 0 && firstListSize > 0) {
            if (position == 0) return FIRST_LIST_HEADER_VIEW;
            else return FIRST_LIST_ITEM_VIEW;
        }

        return super.getItemViewType(position);
    }
}

Il existe un autre moyen de conserver vos éléments dans un seul ArrayList d’objets afin que vous puissiez définir un attribut balisant les éléments afin d’indiquer quel élément appartient à la première liste et quel élément appartient à la deuxième liste. Puis passez ce ArrayList dans votre RecyclerView, puis implémentez la logique à l'intérieur de l'adaptateur pour les peupler de manière dynamique.

J'espère que ça t'as aidé.

38
Reaz Murshed

Il y a quelque temps, je rencontrais un problème similaire. Dans mon cas, la vue du recycleur externe fonctionnait parfaitement, mais l'adaptateur de la vue du recycleur interne/secondaire présentait des problèmes mineurs. Toutes les méthodes, telles que le constructeur, ont été lancées et même la méthode getCount (). était appelé, bien que les méthodes finales responsables de générer une vue, c’est-à-dire ..

1. Les méthodes onBindViewHolder () n'ont jamais été appelées. -> Problème 1.

2. Finalement, quand il a été appelé, il n'affiche jamais la liste des éléments/lignes de la vue du recycleur. -> Problème 2.

Raison pour laquelle cela s'est produit :: Lorsque vous placez une vue du recycleur dans une autre vue, la hauteur du premier/la vue externe du recycleur n'est pas ajustée automatiquement. Il est défini lorsque la première vue/externe est créée, puis reste fixe. À ce stade, votre seconde vue/recycleur interne n'a pas encore chargé ses éléments. Par conséquent, sa hauteur est définie sur zéro et ne change jamais, même lorsqu'elle récupère des données. Ensuite, lorsque onBindViewHolder () dans votre deuxième/vue intérieure du recycleur est appelé , il obtient les éléments mais il n'a pas l'espace nécessaire pour les afficher car sa hauteur est toujours nul. Ainsi, les éléments de la deuxième vue du recycleur ne sont jamais affichés, même si onBindViewHolder () les y a ajoutés.

Solution :: vous devez créer votre LinearLayoutManager personnalisé pour la deuxième vue du recycleur et c'est tout. Pour créer votre propre LinearLayoutManager: Créez une classe Java avec le nom CustomLinearLayoutManager et collez-y le code ci-dessous. NO CHANGEMENTS REQUIS

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

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

vous pouvez utiliser LayoutInflater pour gonfler vos données dynamiques en tant que fichier de présentation.

[~ # ~] mise à jour [~ # ~] : créez d'abord un LinearLayout dans la présentation de votre CardView et attribuez-lui un ID. Après cela, créez un fichier de présentation que vous souhaitez gonfler. enfin dans votre méthode onBindViewHolder dans votre classe "RAdaper". écrivez ces codes:

  mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  view = mInflater.inflate(R.layout.my_list_custom_row, parent, false);

après cela, vous pouvez initialiser les données et ClickListeners avec vos données RAdapter. J'espère que ça aide.

this et this peut être utile :)

8
user5747362