Retaining position in ListView after calling notifyDataSetChanged
Asked Answered
S

7

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?

Stepup answered 26/11, 2011 at 3:50 Comment(3)
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 howHammack
Simple solution: https://mcmap.net/q/390408/-android-listview-add-items-to-top-without-list-view-scroll-duplicateHopper
use @KamranAhmed 's answer. simple and elegant solution. infact correct evenDunfermline
V
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);
Virginium answered 26/11, 2011 at 3:54 Comment(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;Pontificals
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: code.google.com/p/android/issues/detail?id=6741Dalesman
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.Footstool
I used getScrollX() and setScrollX() instead of getFirstVisiblePosition() and setSelectionFromTop(), as that still caused some small jumps when adding content.Leannleanna
I used the (non-delayed) post method to get it to work, unfortunately, it still flickers during update.Intrigue
what is mList? Is it an object of listView class? And where do I put this code, right after listViewObj.setAdapter(adapter)?Diocese
this is complete copy-paste from here, #3014589Lucic
In order to avoid the flickering, I followed the instructions from https://mcmap.net/q/390409/-adding-items-to-listview-maintaining-scroll-position-and-not-seeing-a-scroll-jump/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.Laurasia
setSelectionFromTop call requires minimum API level 21. What about running on pre 21 devices ?Doctrinal
F
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!

Flanch answered 29/11, 2014 at 7:21 Comment(2)
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.Bourges
This is the correct solution, no "hacking" required to get something that simple.Curcio
N
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.

Nicholson answered 3/12, 2014 at 14:45 Comment(0)
H
0

I am using

listView.getFirstVisiblePosition

to maintain last visible position.

Homeopathist answered 31/8, 2015 at 16:7 Comment(0)
D
0

Go with transcript mode of listview.

Designedly answered 23/12, 2015 at 12:33 Comment(0)
M
0

Try this

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

      if (first == true) {
      listview.setAdapter(customAdapter);
      first=false;
      }
      else
      customAdapter.notifyDataSetChanged();
      }
Metritis answered 9/1, 2016 at 11:12 Comment(0)
W
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);  
Windswept answered 14/4, 2019 at 17:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.