Pull to refresh

ListView внутри ScrollView

Development for Android *
Sandbox
В работе над проектом возникла необходимость отрисовать лейаут такого вида: сверху компактным блоком располагаются детали топика, а под ними – список комментариев к этому топику. Сначала оно было реализовано естественным образом: лейаут деталей топика, а под ним – список. Позже ТЗ поменялось, и получилось, что нужно заголовок скролить вместе со списком.

Первым побуждением было сделать то, что вынесено в заголовок: поместить ListView внутрь ScrollView. Получившаяся штука отображалась некорректно, что заставило меня зарыться поглубже в гугл.

Во многих местах в сети (например, тут: https://groups.google.com/forum/#!topic/android-beginners/LRpLgGOy2Pc или тут: http://stackoverflow.com/questions/1526831/android-scrollview-layout-problem) напрямую говорится о том, что так делать нельзя. Для реализации таких вещей у ListView предусмотрены штатные заголовки (headers).

Добавление заголовков производится вызовом метода класса ListView:

public void addHeaderView (View v)

где View v можно создавать любым способом (или явным вызовом конструктора, или через inflater).

View hv = ...;
listView.addHeaderView(hv);


Заголовков у ListView может быть больше одного. С их количеством, кстати, связана ещё одна особенность: по умолчанию заголовки тоже кликабельны, как и обычные элементы списка, клик на них вызывает отработку лиснеров, заданных вызовами setOnItemClickListener и setOnItemLongClickListener, так что позиция кликнутого элемента, которая передаётся в лиснеры в параметре position, будет смещена на количество заголовков. Этот факт нужно обязательно учитывать в лиснерах при обработке. Для этого у ListView есть метод:

int getHeaderViewsCount()

который возвращает количество заголовков списка. На значение, которое он возвращает, можно, например, уменьшить значение position, полученное в лиснере, перед позиционированием во внутреннем списке данных.

protected OnItemClickListener itemClickListener = new OnItemClickListener() {
public void onItemClick(final AdapterView<?> l, final View v, final int position, final long id) {
MyItem myItem = myItems.get(position - listView.getHeaderViewsCount());
// do something to myItem
}
};


Существует ещё один метод для добавления заголовков, чуть более сложный:

public void addHeaderView (View v, Object data, boolean isSelectable)

Он предоставляет возможность сделать заголовок некликабельным: если в третий параметр передать false, то при клике на заголовок не будет отрабатывать onItemClickListener списка. Лиснеры же, заданные для views, расположенных внутри такого заголовка, будут отрабатывать штатно. Однако, даже в этом случае, несмотря на некликабельность заголовка, нумерация элементов для position всё равно будет учитывать их наличие.

View hv = ...;
listView.addHeaderView(hv, null, false);


Для полноты изложения скажу, что вторым параметром (Object data), смысл которого не очень понятен из документации, на самом деле задаются данные, которые будут возвращаться методом Adapter.getItem() (взято тут: http://stackoverflow.com/questions/4311693/what-does-the-second-parameter-in-addheaderview-in-the-class-listview-do).

Аналогичный набор методов есть у списка и для футеров (footers):

void addFooterView(View v)

void addFooterView(View v, Object data, boolean isSelectable)

int getFooterViewsCount()
Tags:
Hubs:
Total votes 24: ↑16 and ↓8 +8
Views 16K
Comments Comments 16