Into Open Source


Don’t loose your position in an Android ListView

Geplaatst in Artikel door Dirk op 16/12/2011

As an Android developer you will certainly have to use a ListView sooner or later. And although it can seem hard to implement at first sight it’s not that hard.
In just a few steps you can have your own implementation:

  1. Extend your activity from an Android ListActivity
  2. Create a class that extends the ArrayAdapter
  3. In that class, implement the getView(int position, View convertView, ViewGroup parent) method

Then from your list activity you can create an adapter to show your list. Basically that’s it.
Loading the data initially into your list is no big deal. However, you may experience that when you change the data, and want to reload it that you loose the position in the list where you were. And that’s exactly what we will solve here!

Different ways of refreshing a ListView

To easily explain all this we will start from an easy example where we will list 20 customer-objects. When loading is done we wait for 5 seconds during which you can scroll as much as you want. After those 5 seconds we update one element (the last one) in the list and refresh the list on the device while trying to keep our scroll position correctly set. So ideally when you try this example, you start the demo application, you scroll all the way down in the list, way a few seconds and you will see that the last element in the list gets updated AND that your scrolling position is still at the bottom (and not reset to the top!). So this is the situation we start from:
public class DemoListViewActivity extends ListActivity {
private List customers;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_view_layout);
loadData(true);
//Create an AsyncTask taht runs for 5 seconds in the background
//During that time you scroll as much as you like
//After the 5 seconds the list is refreshed and we try to keep the scrollposition
AsyncTask task = new AsyncTask() {
@Override
protected Object doInBackground(Object... objects) {
try {
Thread.sleep(5000L); //Wait for 5 seconds and update the data
customers.remove(customers.size() - 1); //Remove the last element
Customer c = new Customer();
c.setFirstName("Google");
c.setLastName("Android");
customers.add(c); //Add a new element add the last place
} catch (InterruptedException e) {}
return null;
}
@Override
protected void onPostExecute(Object object) {
loadData(false);
}
}.execute();
}
private void loadData(boolean createNew) {
if (createNew) {
//ToDO Load some data from a database, local storage or the web
customers = new ArrayList();
for (int i = 0; i < 20; i++) {
Customer c = new Customer();
c.setFirstName("Dummy " + i);
c.setLastName("Object " + i);
customers.add(c);
}
}
}
}

How to loose your position – part 1

The easiest way is to always create a new instance of your adapter and put the list of objects to display on it:
CustomerListAdapater cla = new CustomerListAdapater(DemoListViewActivity.this, customers);
super.setListAdapter(cla);

Adding these lines at the end of the loadData method works, the list gets updated but you always jump back to the top of the list. And that’s not really what we want…

How to loose your position – part 2

Antoher way is to find out what the index of the first displayed list element is, reload the list and then force the list to scroll back to that index you got:
int fistVisiblePosition = getListView().getFirstVisiblePosition(); CustomerListAdapater cla = new CustomerListAdapater(DemoListViewActivity.this, customers); super.setListAdapter(cla); super.setSelection(fistVisiblePosition);
These lines are again added to the end of the loadData method, and this also works. You even keep your scroll position. Or more or less… If you scrolled the list and the first visible item in your list is displayed only half, then after the refresh it will be displayed entirely. So we are close to a good solution, but you feel this isn’t natural.

How to keep your position

But we can do better, more natural! In the previous two examples you saw that we always recreated the adapter. And that is eventually causing the reset of the scroll position. So what if we could keep the adapter and re-use it?
Well let’s try this. We need to add a method to the adapter to set the new data:
public void reload(List customers) {
this.customers.clear();
this.customers.addAll(customers);
notifyDataSetChanged();
}

And afterwards we can get a reference of the adapater in the ListActivity and call the reload method. If no reference is found we create a new adapter (like in the first method):
if (super.getListView().getAdapter() == null) {
CustomerListAdapater cla = new CustomerListAdapater(DemoListViewActivityPart3.this, customers);
super.setListAdapter(cla);
} else {
((CustomerListAdapater)super.getListView().getAdapter())
.reload(customers);
}

Conclusion

It’s not hard at all to change your implementation and keep the scroll position in the future. And if, in some cases, you have to jump back to the top of the list, you can easily re-create the adapter and job done!

Geef een reactie

Fill in your details below or click an icon to log in:

WordPress.com logo

Je reageert onder je WordPress.com account. Log Out / Bijwerken )

Twitter-afbeelding

Je reageert onder je Twitter account. Log Out / Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log Out / Bijwerken )

Verbinden met %s


Follow

Get every new post delivered to your Inbox.