I'm planning to develop an app that shows some dynamic data inside a recyclerCardView
. So i decided add a recyclerView
called CheckBoxRecyclerView
inside my main recyclerView
. This is my code for my app :
My Main Activity :
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{ }
And it's layout file :
<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">
and inside this recyclerView there is another:
<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>
Their adapters, 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(); } }
And second Adapter :
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(); } }
And my problem: as you see in CAdapter, only constructor's Log message displayed.
UPDATE: if there is another way to display some dynamic data inside another dynamic card if yes is it better to use it instead of recyclerView?
anyone help me?
The output : Application output as you see just the setTitle for RAdapter works.
A nested RecyclerView is an implementation of a RecyclerView within a RecyclerView. An example of such a layout can be seen in a variety of apps such as the Play store where the outer (parent) RecyclerView is of Vertical orientation whereas the inner (child) RecyclerViews are of horizontal orientations.
You should not put multiple RecyclerViews inside each other.
I would like to suggest to use a single RecyclerView
and populate your list items dynamically. I've added a github project to describe how this can be done. You might have a look. While the other solutions will work just fine, I would like to suggest, this is a much faster and efficient way of showing multiple lists in a RecyclerView
.
The idea is to add logic in your onCreateViewHolder
and onBindViewHolder
method so that you can inflate proper view for the exact positions in your RecyclerView
.
I've added a sample project along with that wiki too. You might clone and check what it does. For convenience, I am posting the adapter that I have used.
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); } }
There is another way of keeping your items in a single ArrayList
of objects so that you can set an attribute tagging the items to indicate which item is from first list and which one belongs to second list. Then pass that ArrayList
into your RecyclerView
and then implement the logic inside adapter to populate them dynamically.
Hope that helps.
I ran into similar problem a while back and what was happening in my case was the outer recycler view was working perfectly fine but the the adapter of inner/second recycler view had minor issues all the methods like constructor got initiated and even getCount() method was being called, although the final methods responsible to generate view ie..
1. onBindViewHolder() methods never got called. --> Problem 1.
2. When it got called finally it never show the list items/rows of recycler view. --> Problem 2.
Reason why this happened :: When you put a recycler view inside another recycler view, then height of the first/outer recycler view is not auto adjusted. It is defined when the first/outer view is created and then it remains fixed. At that point your second/inner recycler view has not yet loaded its items and thus its height is set as zero and never changes even when it gets data. Then when onBindViewHolder() in your second/inner recycler view is called, it gets items but it doesn't have the space to show them because its height is still zero. So the items in the second recycler view are never shown even when the onBindViewHolder() has added them to it.
Solution :: you have to create your custom LinearLayoutManager for the second recycler view and that is it. To create your own LinearLayoutManager: Create a Java class with the name CustomLinearLayoutManager
and paste the code below into it. NO CHANGES REQUIRED
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(); } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With