Простой способ объединять несколько списков в один (с заголовками)

Jul 8, 2013   #без категории 

Возникла задача объединения нескольких списков из разных источников.

Пусть даны списки:

Заголовок 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;
  }
});
comments powered by Disqus