Разделите элементы на группы в RecyclerView
Мне нужно разделить элементы в RecyclerView на группы с заголовками (как в приложении "Входящие" на рисунке ниже), поэтому помогите мне выяснить, какой подход был бы лучше для моего случая: 1) Я могу использовать гетерогенные макеты для него, но это не так удобно вставлять новые элементы в группы (потому что мне нужно проверить, уже добавлены ли элементы той же группы или мне нужно добавить новый разделитель). Поэтому в этом случае все операции с такой структурой данных я оберну в отдельный класс.
2) Теоретически я могу обернуть каждую группу в свой собственный RecyclerView с меткой, это хорошая идея?
2 ответа
Например, вы можете:
Использовать
TreeMap<Date,List<Event>>
для разделения элементов по дате. Это будет коллекция для хранения ваших бизнес-объектов. Конечно, если у вас уже есть подобная структура, вы можете сохранить ее. Просто важно иметь что-то для простого создания списка элементов для заполнения пользовательского интерфейса с правильным порядком элементов.Определите выделенный абстрактный тип для
List
предметы (например,ListItem
) чтобы обернуть ваши бизнес-объекты. Его реализация может быть примерно такой:public abstract class ListItem { public static final int TYPE_HEADER = 0; public static final int TYPE_EVENT = 1; abstract public int getType(); }
Определите класс для каждого типа элемента List (здесь я добавил только два типа, но вы можете использовать их по мере необходимости):
public class HeaderItem extends ListItem { private Date date; // here getters and setters // for title and so on, built // using date @Override public int getType() { return TYPE_HEADER; } } public class EventItem extends ListItem { private Event event; // here getters and setters // for title and so on, built // using event @Override public int getType() { return TYPE_EVENT; } }
Создайте список следующим образом (где mEventsMap - это построение карты в точке 1):
List<ListItem> mItems; // ... mItems = new ArrayList<>(); for (Date date : mEventsMap.keySet()) { HeaderItem header = new HeaderItem(); header.setDate(date); mItems.add(header); for (Event event : mEventsMap.get(date)) { EventItem item = new EventItem(); item.setEvent(event); mItems.add(item); } }
Определите адаптер для вашего
RecyclerView
, работа надList
определено в пункте 4. Здесь важно переопределитьgetItemViewType
метод следующим образом:@Override public int getItemViewType(int position) { return mItems.get(position).getType(); }
Тогда вам нужно иметь два макета и ViewHolder для элементов заголовка и события. Методы адаптера должны позаботиться об этом соответственно:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == ListItem.TYPE_HEADER) { View itemView = mLayoutInflater.inflate(R.layout.view_list_item_header, parent, false); return new HeaderViewHolder(itemView); } else { View itemView = mLayoutInflater.inflate(R.layout.view_list_item_event, parent, false); return new EventViewHolder(itemView); } } @Override public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) { int type = getItemViewType(position); if (type == ListItem.TYPE_HEADER) { HeaderItem header = (HeaderItem) mItems.get(position); HeaderViewHolder holder = (HeaderViewHolder) viewHolder; // your logic here } else { EventItem event = (EventItem) mItems.get(position); EventViewHolder holder = (EventViewHolder) viewHolder; // your logic here } }
Здесь это репозиторий на GitHub, обеспечивающий реализацию подхода, описанного выше.
Вы можете попробовать использовать библиотеку, которую я написал, чтобы решить эту проблему в моем проекте. Зависимость Gradle (требуется репозиторий jcenter):
dependencies {
//your other dependencies
compile 'su.j2e:rv-joiner:1.0.3'//latest version by now
}
Тогда в вашей ситуации вы можете сделать что-то вроде этого:
//init your RecyclerView as usual
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
//construct a joiner
RvJoiner rvJoiner = new RvJoiner();
rvJoiner.add(new JoinableLayout(R.layout.today));
YourAdapter todayAdapter = new YourAdapter();
rvJoiner.add(new JoinableAdapter(todayAdapter));
rvJoiner.add(new JoinableLayout(R.layout.yesterday));
YourAdapter yesterdayAdapter = new YourAdapter();
rvJoiner.add(new JoinableAdapter(yesterdayAdapter));
//set join adapter to your RecyclerView
rv.setAdapter(rvJoiner.getAdapter());
Когда вам нужно добавить элемент, добавьте его в соответствующий адаптер, например:
if (timeIsToday) {
todayAdapter.addItem(item);//or other func you've written
} else if (timeIsYesterday) {
yesterdayAdapter.addItem(item);
}
Если вам нужно динамически добавить новую группу в представление переработчика, вы можете использовать следующие методы:
rvJoiner.add(new JoinableLayout(R.layout.tomorrow));
YourAdapter tomorrowAdapter = new YourAdapter();
rvJoiner.add(new JoinableAdapter(tomorrowAdapter));
Вы можете проверить эту ссылку для более подробного описания библиотеки. Я не могу сказать, что это, безусловно, лучший способ достичь цели, но иногда мне это помогает.
UPD:
Я нашел способ сделать это без использования внешних библиотек. Используйте класс RecyclerView.ItemDecoration. Например, чтобы сгруппировать элементы по 3 элементам в группе, вы можете сделать это:
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
private int textSize = 50;
private int groupSpacing = 100;
private int itemsInGroup = 3;
private Paint paint = new Paint();
{
paint.setTextSize(textSize);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
if (position % itemsInGroup == 0) {
c.drawText("Group " + (position / itemsInGroup + 1), view.getLeft(),
view.getTop() - groupSpacing / 2 + textSize / 3, paint);
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) % itemsInGroup == 0) {
outRect.set(0, groupSpacing, 0, 0);
}
}
});
Надеюсь, поможет.
Этот вопрос задан еще в 2016 году, а тем временем (2020 год) доступны разные библиотеки для группировки представлений ресайклера. Самые популярные:
- Поклонница
- Эпоксидная смола (от airbnb)