Pull to refresh

Фоновая подгрузка списков

Development for Android *
Sandbox
При загрузке больших списков из медленного источника (например HTTP запрос) нет смысла загружать весь список сразу, особенно если пользователю потребуется лишь небольшое количество данных (результаты поиска, новостные статьи). В этом случае целесообразно реализовать постраничную подгрузку списка. В этой статье показан простой способ с использованием footer view контрола ListView. Подразумевается, что у нас уже есть список и его адаптер, который загружает элементы (пусть несколько первых, или весь) используя например ассинхронный HTTP запрос. Удобнее вынести запрос данных в отдельный класс, в котором добавить возможность подгрузки очередной страницы.

План действий


  1. Подготавливаем layout для элемента сообщения о процессе загрузки
  2. Подготавливаем контрол ListView
  3. Добавляем в адаптер списка код запуска загрузки следующей страницы
  4. Обрабатываем результат загрузки очередной страницы


Подготавливаем 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();
	
Tags:
Hubs:
Total votes 26: ↑23 and ↓3 +20
Views 10K
Comments 6
Comments Comments 6

Posts