Возникла задача объединения нескольких списков из разных источников.
Пусть даны списки:
Заголовок 1
====
Элемент 1
Элемент 2
...
Элемент N
Заголовок 2
====
Элемент 1
Элемент 2
...
Элемент K
Заголовок 3
====
Элемент 1
Элемент 2
...
Элемент M
Первый список из БД, второй список — из скачанного с интернета массива, третий — сгенерированный вручную из массива.
Каждый список {Элемент 1, …, Элемент Х} содержится в собственном курсоре (сгенерировать курсор из массива можно с помощью MatrixCursor).
Пусть стоит задача отобразить ListView, в котором последовательно отображены элементы всех списков, причём разделённые заголовками.
Сначала я навелосипедил свой NestedAdapter. Исходник тут: http://pastebin.com/MyrRxmWf
Этот велосипед вроде даже работает, поддерживает списки как с заголовками, так и без. На вход принимает пары <адаптер, заголовок>, каждый из адаптеров наследует CursorAdapter. Для отображения контента адаптеров он использует их методы getView, для отображения заголовков — стандартный андроидный simple_list_item_1.
Кстати, у него есть много минусов. Он не умеет отслеживать изменения в курсорах, поэтому при изменении данных в курсоре будут показываться неправильные данные (а можно словить и креш). Поэтому им лучше без доработки не пользоваться.
Вариант неплохой, но у меня возникло слишком много сомнений в его разумности.
Лёгкое гугление предложило мне MergeCursor. Это курсор, который умеет склеивать данные из нескольких курсоров в линейный список.
Так вот в нём не хватает заголовков и не очень понятно сначала, как их добавить.
Решение простое. Создаём на основе уже упомянутого MatrixCursor курсор из списка с одним элементом.
public static class TitleCursor extends MatrixCursor {
public static final String ID = "_id";
public static final String TITLE = "cursor_title";
public TitleCursor(String title) {
super(new String[]{ID, TITLE});
RowBuilder builder = newRow();
builder.add(0); // ID
builder.add(title); // TITLE
}
}
Важное замечание: обязательно нужен столбец с названием _id, потому что на него полагается стандартная реализация CursorAdapter.
Вот и всё =)
Используется это дело примерно так:
MyCursorAdapter.TitleCursor title1 = new MyCursorAdapter.TitleCursor("Заголовок 1");
MyCursorAdapter.TitleCursor title2 = new MyCursorAdapter.TitleCursor("Заголовок 2");
MyCursorAdapter.TitleCursor title3 = new MyCursorAdapter.TitleCursor("Заголовок 3");
Cursor cursor1 = ...;
Cursor cursor2 = ...;
Cursor cursor3 = ...;
MergeCursor cursor = new MergeCursor(
new Cursor[] {
title1, cursor1,
title2, cursor2,
title3, cursor3
});
Ну а потом просто отображаем этот курсор в ListView любимым адаптером.
UPD. А есть ещё более простой способ. Используем ExpandableListView, для которого вызываем expandGroup для всех групп и переопределяем OnGroupClickListener, чтобы не давать группам свернуться:
getExpandableListView().setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
return true;
}
});