android – Is there a way to prevent the listview from scrolling to its top position when its adapter's data is changed?-ThrowExceptions

Exception or error:

I’m having a bit of trouble preserving the scroll position of a list view when changing it’s adapter’s data.

What I’m currently doing is to create a custom ArrayAdapter (with an overridden getView method) in the onCreate of a ListFragment, and then assign it to its list:

mListAdapter = new CustomListAdapter(getActivity());
mListAdapter.setNotifyOnChange(false);
setListAdapter(mListAdapter);

Then, when I receive new data from a loader that fetches everything periodically, I do this in its onLoadFinished callback:

mListAdapter.clear();
mListAdapter.addAll(data.items);
mListAdapter.notifyDataSetChanged();

The problem is, calling clear() resets the listview’s scroll position. Removing that call preserves the position, but it obviously leaves the old items in the list.

What is the proper way to do this?

How to solve:

As you pointed out yourself, the call to ‘clear()’ causes the position to be reset to the top.

Fiddling with scroll-position, etc. is a bit of a hack to get this working.

If your CustomListAdapter subclasses from ArrayAdapter, this could be the issue:

The call to clear(), calls ‘notifyDataSetChanged()’. You can prevent this:

mListAdapter.setNotifyOnChange(false); // Prevents 'clear()' from clearing/resetting the listview
mListAdapter.clear();
mListAdapter.addAll(data.items);
// note that a call to notifyDataSetChanged() implicitly sets the setNotifyOnChange back to 'true'!
// That's why the call 'setNotifyOnChange(false) should be called first every time (see call before 'clear()').
mListAdapter.notifyDataSetChanged(); 

I haven’t tried this myself, but try it 🙂

###

Check out: Maintain/Save/Restore scroll position when returning to a ListView

Use this to save the position in the ListView before you call .clear(), .addAll(), and . notifyDataSetChanged().

int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();

After updating the ListView adapter, the Listview’s items will be changed and then set the new position:

mList.setSelectionFromTop(index, top);

Basically you can save you position and scroll back to it, save the ListView state or the entire application state.

Other helpful links:

Save Position:
How to save and restore ListView position in Android

Save State:
Android ListView y position

Regards,

Please let me know if this helps!

###

There is one more use-case I came across recently (Android 8.1) – caused by bug in Android code. If I use mouse-wheel to scroll list view – consecutive adapter.notifyDataSetChanged() resets scroll position to zero. Use this workaround until bug gets fixed in Android

        listView.onTouchModeChanged(true); // workaround
        adapter.notifyDataSetChanged();

More details is here: https://issuetracker.google.com/u/1/issues/130103876

###

In your Expandable/List Adapter, put this method

public void refresh(List<MyDataClass> dataList) {
    mDataList.clear();
    mDataList.addAll(events);
    notifyDataSetChanged();
}

And from your activity, where you want to update the list, put this code

if (mDataListView.getAdapter() == null) {
    MyDataAdapter myDataAdapter = new MyDataAdapter(mContext, dataList);
    mDataListView.setAdapter(myDataAdapter);
} else {
    ((MyDataAdapter)mDataListView.getAdapter()).refresh(dataList);
}

In case of Expandable List View, you will use
mDataListView.getExpandableListAdapter() instead of
mDataListView.getAdapter()

Leave a Reply

Your email address will not be published. Required fields are marked *