43

I'm using an OnScrollListener to dynamically add items to a ListView when the user scrolls to the bottom. After I add the data to the adapter and call notifyDataSetChanged though, the ListView goes back up to the top. Ideally, I would like to retain the position in the ListView. Any thoughts on how I should go about doing this?

SeanPONeil
  • 3,851
  • 4
  • 27
  • 39
  • Hi can you please share how u have retain and used current state of list view in OnScrollListener... i am doing same, but i update list from Async task........ not working some how – ANUP May 22 '13 at 22:21
  • 3
    Simple solution: http://stackoverflow.com/a/28149739/1468093 – Kamran Ahmed Jan 26 '15 at 12:09
  • use @KamranAhmed 's answer. simple and elegant solution. infact correct even – Ankan-Zerob Dec 24 '15 at 16:41

7 Answers7

98

Could this be what you want?

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

// notify dataset changed or re-assign adapter here

// restore the position of listview
mList.setSelectionFromTop(index, top);

EDIT 28/09/2017:

The API has changed quite a bit since 2015. It is similar, but now it would be:

// save index and top position
int index = mList.FirstVisiblePosition; //This changed
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.Top; //this changed

// notify dataset changed or re-assign adapter here

// restore the position of listview
mList.setSelectionFromTop(index, top);
Marshall
  • 1,273
  • 3
  • 16
  • 37
iCantSeeSharp
  • 3,905
  • 4
  • 37
  • 62
  • 9
    Thanks. If you want to add items to the top of the list without seeing the update happen, you'll need to add the number of items you added to the index int. int index = mList.getFirstVisiblePosition() + NUMBER_OF_ADDED_ITEMS; – Lior Iluz Aug 14 '12 at 15:50
  • 5
    If setSelection doesn't work immediately after calling notifyDataSetChanged, here's a workaround: getListView().postDelayed(new Runnable() { @Override public void run() { getListView().setSelection(8); } }, 500); Android bug is supposedly assigned, so this should be fixed at some point: https://code.google.com/p/android/issues/detail?id=6741 – d2vid Jul 17 '13 at 23:05
  • 4
    Working perfectly. Also, incase it helps someone else, this wasn't working at first for me. Till I realised in some previous infinite wisdom of mine, i had added `lv.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL)`. Don't do that... it will forcefully scroll to the bottom of your listview until you've pulled most of your hair out. hah. – wired00 Feb 05 '14 at 00:41
  • 2
    I used getScrollX() and setScrollX() instead of getFirstVisiblePosition() and setSelectionFromTop(), as that still caused some small jumps when adding content. – Micky Jun 05 '14 at 09:54
  • 1
    I used the (non-delayed) post method to get it to work, unfortunately, it still flickers during update. – Czechnology Jul 13 '14 at 19:17
  • what is mList? Is it an object of `listView` class? And where do I put this code, right after `listViewObj.setAdapter(adapter)`? – Apurva Feb 04 '15 at 16:19
  • this is complete copy-paste from here, http://stackoverflow.com/questions/3014089/maintain-save-restore-scroll-position-when-returning-to-a-listview – Sruit A.Suk Mar 02 '15 at 05:14
  • Thanks! This was what I needed too! – Antonis427 Jun 17 '15 at 07:00
  • In order to avoid the flickering, I followed the instructions from http://stackoverflow.com/q/19320214/2343626 . What you have to do is you have to register a OnPreDrawListener to the listview and then you can ignore the draw request made by the adapter and allow only the draw request that was made when you called setSelectionFromTop. – Luccas Correa Jul 07 '15 at 22:00
  • setSelectionFromTop call requires minimum API level 21. What about running on pre 21 devices ? – Bhargav Thanki Dec 29 '16 at 09:20
6

The situation:

When you set an adapter to your listview, it refreshes its state. So, normally scrolls up automatically.

The solution:

Assign an adapter to listview if it has not any, else only update the dataset of the assigned adapter, not re-set it to your listview.

A detailed tutorial is explained at the following link;

Android ListView: Maintain your scroll position when you refresh

Good Luck!

Taner
  • 4,479
  • 3
  • 16
  • 14
  • 1
    This works in a way as the scroll position doesn't move if i add an additional item but the list within the view jumps down to accommodate the new item. – Simon May 18 '15 at 18:20
  • This is the correct solution, no "hacking" required to get something that simple. – Ethenyl Sep 10 '15 at 15:54
0

I implemented the postDelayed and I was getting flickering upon refresh. I search some more and found out that I was doing things wrong. Basically I should not create a new adapter every time I wanted to change the data. I ended up doing it this way and it works:

//goes into your adapter
public void repopulateData(String[] objects) {
    this.objects = null;
    this.objects = objects;
    notifyDataSetChanged();
}

//goes into your activity or list
if (adapter == null) {
    adapter = new Adapter();
} else {
    adapter.repopulateData((String[])data);
}

Hope this helps.

MegaChan
  • 160
  • 1
  • 1
  • 4
0

I am using

listView.getFirstVisiblePosition

to maintain last visible position.

Ajay
  • 4,520
  • 2
  • 28
  • 42
0

Go with transcript mode of listview.

stdout
  • 1,863
  • 1
  • 23
  • 33
0

Try this

      boolean first=true; 
      protected void onPostExecute(Void result) 
      {

      if (first == true) {
      listview.setAdapter(customAdapter);
      first=false;
      }
      else
      customAdapter.notifyDataSetChanged();
      }
0

Here is the code:

// Save the ListView state (= includes scroll position) as a Parceble
Parcelable state = listView.onSaveInstanceState();

// e.g. set new items
listView.setAdapter(adapter);

// Restore previous state (including selected item index and scroll position)
listView.onRestoreInstanceState(state);  
Robson
  • 766
  • 5
  • 20
  • 36