When downloading large lists from a slow source (for example, an HTTP request), it makes no sense to download the entire list at once, especially if the user needs only a small amount of data (search results, news articles). In this case, it is advisable to implement paginal upload list. This article shows a simple way using the
ListView footer view . It is implied that we already have a list and its adapter, which loads the elements (even the first few, or all) using for example an asynchronous HTTP request. It is more convenient to make a request for data in a separate class in which to add the ability to load the next page.
Action plan
- Prepare layout for the boot process message item.
- Preparing a ListView control
- Add the following page startup code to the list adapter
- Processing the result of loading the next page
Prepare layout for the boot process message item.
Create a layout xml with two
ProgressBar elements with the
indeterminateOnly flag set and a
TextView with the text “Loading ...”. Put them in a horizontal LinearLayout. You can also use a ready-made layout for an empty list for boot information.
Preparing ListView
Load the prepared layout and set it as a basement for our ListView. This must be done before calling the setAdapter method. Do not forget to save the link to the item to delete when you reach the end of the list.
')
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); ... }
Add the following page startup code to the list adapter
To start loading the next page, we use the
list adapter's
getView method: if the last element of the list is requested, then it is time to load the next page. The download process is conveniently removed in a separate thread and then throw Intent into the current activation. Do not forget to call
notifyDataSetInvalidated if during loading the data accessed by the adapter will change.
@Override public View getView(int position, View convertView, ViewGroup parent) { ... if (position == getCount() - 1 && hasNextPage()) { loadNextPage(); } }
UPD: grishkaa recommended a more correct way using
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(); } } });
Processing the result of loading the next page
Intent can be intercepted by anonymous
BroadcastReceiver and sent to
Handler (to execute in a UI thread). All that remains is to call the adapter's
notifyDataSetChanged method and remove the basement element if the last page is loaded.
if (!hasNextPage() && mList.getFooterViewsCount() > 0) { mList.removeFooterView(mLoadingFooter); } mAdapter.notifyDataSetChanged();