При загрузке больших списков из медленного источника (например HTTP запрос) нет смысла загружать весь список сразу, особенно если пользователю потребуется лишь небольшое количество данных (результаты поиска, новостные статьи). В этом случае целесообразно реализовать постраничную подгрузку списка. В этой статье показан простой способ с использованием footer view контрола ListView. Подразумевается, что у нас уже есть список и его адаптер, который загружает элементы (пусть несколько первых, или весь) используя например ассинхронный HTTP запрос. Удобнее вынести запрос данных в отдельный класс, в котором добавить возможность подгрузки очередной страницы.
Создаем layout xml с двумя элементами ProgressBar с установленным флагом indeterminateOnly и TextView с текстом «Идет загрузка...». Помещаем их в горизонтальный LinearLayout. Можно использовать и уже готовый layout для пустого списка для информации о начальной загрузке.
Загружаем подготовленный layout и устанавливаем его подвальным для нашего ListView. Это надо сделать до вызова метода setAdapter. Не забудем сохранить ссылку на элемент, чтобы удалить при достижении конца списка.
Для запуска загрузки следующей страницы используем метод getView адаптера списка: если затребован последний элемент списка, то время грузить следующую страницу. Процесс загрузки удобно убрать в отдельный тред и затем кинуть Intent в текущую активити. Не забудем вызывать notifyDataSetInvalidated если во время загрузки данные, к которым обращается адаптер будут изменяться.
UPD: grishkaa рекомендовал более правильный способ с использованием AbsListView.OnScrollListener:
Intent можно перехватить анонимным BroadcastReceiver и передать в Handler (чтобы исполнить в UI треде). Нам остается только вызвать метод notifyDataSetChanged адаптера и удалить подвальный элемент, если загружена последняя страница.
План действий
- Подготавливаем layout для элемента сообщения о процессе загрузки
- Подготавливаем контрол ListView
- Добавляем в адаптер списка код запуска загрузки следующей страницы
- Обрабатываем результат загрузки очередной страницы
Подготавливаем layout для элемента сообщения о процессе загрузки
Создаем layout xml с двумя элементами ProgressBar с установленным флагом indeterminateOnly и TextView с текстом «Идет загрузка...». Помещаем их в горизонтальный LinearLayout. Можно использовать и уже готовый layout для пустого списка для информации о начальной загрузке.
Подготавливаем ListView
Загружаем подготовленный layout и устанавливаем его подвальным для нашего ListView. Это надо сделать до вызова метода setAdapter. Не забудем сохранить ссылку на элемент, чтобы удалить при достижении конца списка.
private LinearLayout mLoadingFooter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLoadingFooter = (LinearLayout) layoutInflater.inflate(R.layout.loading, null);
mList = (ListView) findViewById(R.id.list);
mList.addFooterView(mLoadingFooter);
...
}
Добавляем в адаптер списка код запуска загрузки следующей страницы
Для запуска загрузки следующей страницы используем метод getView адаптера списка: если затребован последний элемент списка, то время грузить следующую страницу. Процесс загрузки удобно убрать в отдельный тред и затем кинуть Intent в текущую активити. Не забудем вызывать notifyDataSetInvalidated если во время загрузки данные, к которым обращается адаптер будут изменяться.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
if (position == getCount() - 1 && hasNextPage()) {
loadNextPage();
}
}
UPD: grishkaa рекомендовал более правильный способ с использованием AbsListView.OnScrollListener:
mList.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView arg0, int arg1) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (visibleItemCount > 0 && firstVisibleItem + visibleItemCount == totalItemCount &&
hasNextPage()) {
loadNextPage();
}
}
});
Обрабатываем результат загрузки очередной страницы
Intent можно перехватить анонимным BroadcastReceiver и передать в Handler (чтобы исполнить в UI треде). Нам остается только вызвать метод notifyDataSetChanged адаптера и удалить подвальный элемент, если загружена последняя страница.
if (!hasNextPage() && mList.getFooterViewsCount() > 0) {
mList.removeFooterView(mLoadingFooter);
}
mAdapter.notifyDataSetChanged();