Pull to refresh

ListView внутри ScrollView

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

Первым побуждением было сделать то, что вынесено в заголовок: поместить 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:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.