Android

How to add a recyclerView inside another recyclerView

Как добавить RecyclerView внутри другого RecyclerView

Я планирую разработать приложение, которое показывает некоторые динамические данные внутри recyclerCardView. Поэтому я решил добавить recyclerView called CheckBoxRecyclerView внутри моего main recyclerView. Это мой код для моего приложения :

Моя основная деятельность :

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{

}

И это файл макета :

<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">

и внутри этого RecyclerView есть другой:

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

Их адаптеры, 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();
}
}

И второй адаптер :

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

}

И моя проблема: как вы видите в CAdapter, отображается только сообщение журнала конструктора.

ОБНОВЛЕНИЕ: если есть другой способ отображения некоторых динамических данных внутри другой динамической карты, если да, то лучше ли использовать его вместо RecyclerView?
кто-нибудь мне поможет?
Результат: Вывод приложения как вы видите, работает только setTitle для RAdapter.

Переведено автоматически
Ответ 1

Я хотел бы предложить использовать single RecyclerView и динамически заполнять элементы вашего списка. Я добавил проект на github, чтобы описать, как это можно сделать. Вы могли бы взглянуть. Хотя другие решения будут работать просто отлично, я хотел бы предложить, это гораздо более быстрый и эффективный способ отображения нескольких списков в RecyclerView.

Идея состоит в том, чтобы добавить логику в ваш метод onCreateViewHolder and onBindViewHolder, чтобы вы могли создать правильное представление для точных позиций в вашем RecyclerView.

Я добавил пример проекта вместе с этой вики. Вы можете клонировать и проверить, что он делает. Для удобства я публикую адаптер, который я использовал.

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

Есть еще один способ сохранить ваши элементы в одном ArrayList наборе объектов, так что вы можете установить атрибут, помечающий элементы, чтобы указать, какой элемент из первого списка, а какой из них принадлежит второму списку. Затем передайте это ArrayList в свой RecyclerView, а затем реализуйте логику внутри адаптера для их динамического заполнения.

Надеюсь, это поможет.

Ответ 2

Некоторое время назад я столкнулся с аналогичной проблемой, и в моем случае происходило то, что внешний вид recycler работал отлично, но у адаптера внутреннего / второго вида recycler были незначительные проблемы, все методы, такие как constructor, были инициированы и даже вызывался метод getCount (), хотя конечные методы, ответственные за создание view ie..

1. Методы onBindViewHolder() так и не были вызваны. --> Проблема 1.

2. Когда он был вызван окончательно, он никогда не показывал элементы списка / строки recycler view. --> Проблема 2.

Причина, по которой это произошло :: Когда вы помещаете представление recycler внутри другого представления recycler, высота первого / внешнего представления recycler не регулируется автоматически. Это определяется при создании первого / внешнего представления, а затем остается фиксированным. На этом этапе ваш второй / внутренний вид recycler view еще не загрузил свои элементы, и поэтому его высота устанавливается равной нулю и никогда не меняется, даже когда он получает данные. Затем, когда onBindViewHolder() в вашем втором / внутреннем представлении recycler вызывается, он получает элементы, но у него нет места для их отображения, потому что его высота по-прежнему равна нулю. Таким образом, элементы во втором представлении recycler никогда не отображаются, даже если onBindViewHolder() добавил их в него.

Решение :: вам нужно создать свой пользовательский LinearLayoutManager для второго представления recycler, и все. Чтобы создать свой собственный LinearLayoutManager: Создайте класс Java с именем 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(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();
}
}
}
Ответ 3

вы можете использовать LayoutInflater для расширения ваших динамических данных в виде файла макета.

ОБНОВЛЕНИЕ : сначала создайте LinearLayout внутри макета вашего CardView и присвоите ему идентификатор. после этого создайте файл макета, который вы хотите увеличить. наконец-то в вашем onBindViewHolder методе в вашем классе "RAdaper". напишите эти коды :

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

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

после этого вы можете инициализировать data и ClickListeners вашими данными RAdapter. надеюсь, это поможет.

это и это может быть полезно :)

Ответ 4

Я только что добавил app:layoutManager, похоже, это сработало. Без этого у меня не было экземпляра элемента, хотя мой getItemCount() не был равен нулю. Может быть, это функция более позднего Android?

Внешний RecyclerView

<androidx.recyclerview.widget.RecyclerView
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:id="@+id/outer_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:scrollbars="vertical"
android:isScrollContainer="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" >
</androidx.recyclerview.widget.RecyclerView>

Внутренний RecyclerView

  <androidx.recyclerview.widget.RecyclerView
android:id="@+id/inner_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">
java android android-recyclerview