Сохранение / сохранение / восстановление позиции прокрутки при возврате в ListView - PullRequest
385 голосов
/ 10 июня 2010

У меня есть длинный ListView, который пользователь может прокручивать, прежде чем вернуться к предыдущему экрану.Когда пользователь снова откроет ListView, я хочу, чтобы список был прокручен до той же точки, что и ранее.Есть идеи, как этого добиться?

Ответы [ 19 ]

625 голосов
/ 14 июня 2010

Попробуйте это:

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

// ...

// restore index and position
mList.setSelectionFromTop(index, top);

Объяснение:

ListView.getFirstVisiblePosition() возвращает наиболее заметный элемент списка.Но этот элемент может быть частично прокручен вне поля зрения, и если вы хотите восстановить точную позицию прокрутки в списке, вам нужно получить это смещение.Таким образом, ListView.getChildAt(0) возвращает View для верхнего элемента списка, а затем View.getTop() - mList.getPaddingTop() возвращает его относительное смещение от вершины ListView.Затем, чтобы восстановить позицию прокрутки ListView, мы вызываем ListView.setSelectionFromTop() с индексом элемента, который мы хотим, и смещением для позиционирования его верхнего края от вершины ListView.

531 голосов
/ 16 апреля 2011
Parcelable state;

@Override
public void onPause() {    
    // Save ListView state @ onPause
    Log.d(TAG, "saving listview state @ onPause");
    state = listView.onSaveInstanceState();
    super.onPause();
}
...

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // Set new items
    listView.setAdapter(adapter);
    ...
    // Restore previous state (including selected item index and scroll position)
    if(state != null) {
        Log.d(TAG, "trying to restore listview state..");
        listView.onRestoreInstanceState(state);
    }
}
52 голосов
/ 17 апреля 2011

Я принял решение, предложенное @ (Кирк Волл), и оно работает для меня.Я также видел в исходном коде Android для приложения «Контакты», что они используют похожую технику.Я хотел бы добавить еще несколько деталей: в верхней части моего класса, производного от ListActivity:

private static final String LIST_STATE = "listState";
private Parcelable mListState = null;

Затем некоторые методы переопределяют:

@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    mListState = state.getParcelable(LIST_STATE);
}

@Override
protected void onResume() {
    super.onResume();
    loadData();
    if (mListState != null)
        getListView().onRestoreInstanceState(mListState);
    mListState = null;
}

@Override
protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    mListState = getListView().onSaveInstanceState();
    state.putParcelable(LIST_STATE, mListState);
}

Конечно, «loadData» - моя функциячтобы извлечь данные из БД и поместить их в список.

На моем устройстве Froyo это работает как при изменении ориентации телефона, так и при редактировании элемента и возвращении к списку.

26 голосов
/ 10 июня 2010

Очень простой способ:

/** Save the position **/
int currentPosition = listView.getFirstVisiblePosition();

//Here u should save the currentPosition anywhere

/** Restore the previus saved position **/
listView.setSelection(savedPosition);

Метод setSelection сбросит список до предоставленного элемента.Если не в сенсорном режиме, элемент будет фактически выбран, если в сенсорном режиме элемент будет размещен только на экране.

Более сложный подход:

listView.setOnScrollListener(this);

//Implements the interface:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
    mCurrentX = view.getScrollX();
    mCurrentY = view.getScrollY();
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

//Save anywere the x and the y

/** Restore: **/
listView.scrollTo(savedX, savedY);
17 голосов
/ 28 декабря 2011

Я нашел что-то интересное об этом.

Я попробовал setSelection и scrolltoXY, но он не работал вообще, список остался в том же положении, после некоторых проб и ошибок я получил следующий код, который работает

final ListView list = (ListView) findViewById(R.id.list);
list.post(new Runnable() {            
    @Override
    public void run() {
        list.setSelection(0);
    }
});

Если вместо публикации Runnable вы пытаетесь запустить runOnUiThread, он тоже не работает (по крайней мере, на некоторых устройствах)

Это очень странный обходной путь для чего-то, что должно быть прямым.

10 голосов
/ 26 мая 2013

ВНИМАНИЕ !! В AbsListView есть ошибка, которая не позволяет onSaveState () работать правильно, если ListView.getFirstVisiblePosition () равен 0.

Итак, если у вас есть большие изображения, которые занимают большую часть экрана, и вы прокручиваете до второго изображения, но показывает немного первого, положение прокрутки не будет сохранено ...

из AbsListView.java: 1650 (комментарии мои)

// this will be false when the firstPosition IS 0
if (haveChildren && mFirstPosition > 0) {
    ...
} else {
    ss.viewTop = 0;
    ss.firstId = INVALID_POSITION;
    ss.position = 0;
}

Но в этой ситуации «верх» в приведенном ниже коде будет отрицательным числом, что вызывает другие проблемы, препятствующие правильному восстановлению состояния. Поэтому, когда «верх» отрицателен, возьмите следующего ребенка

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

if (top < 0 && getChildAt(1) != null) {
    index++;
    v = getChildAt(1);
    top = v.getTop();
}
// parcel the index and top

// when restoring, unparcel index and top
listView.setSelectionFromTop(index, top);
6 голосов
/ 27 февраля 2015
private Parcelable state;
@Override
public void onPause() {
    state = mAlbumListView.onSaveInstanceState();
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();

    if (getAdapter() != null) {
        mAlbumListView.setAdapter(getAdapter());
        if (state != null){
            mAlbumListView.requestFocus();
            mAlbumListView.onRestoreInstanceState(state);
        }
    }
}

Этого достаточно

4 голосов
/ 25 августа 2016

Для тех, кто ищет решение этой проблемы, корень проблемы может быть в том, где вы настраиваете свой адаптер представления списка.После того, как вы установите адаптер в списке, он сбрасывает позицию прокрутки.Просто кое-что рассмотреть.Я переместил установку адаптера в свой onCreateView после того, как мы получили ссылку на просмотр списка, и это решило проблему для меня.=) * * Тысяча одна

1 голос
/ 17 июля 2017

Вы можете сохранить состояние прокрутки после перезагрузки, если сохраните состояние до перезагрузки и восстановите его после. В моем случае я сделал асинхронный сетевой запрос и перезагрузил список в обратном вызове после его завершения. Здесь я восстанавливаю состояние. Пример кода - Котлин.

val state = myList.layoutManager.onSaveInstanceState()

getNewThings() { newThings: List<Thing> ->

    myList.adapter.things = newThings
    myList.layoutManager.onRestoreInstanceState(state)
}
1 голос
/ 13 августа 2016

ЛУЧШЕЕ РЕШЕНИЕ:

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

// ...

// restore index and position
mList.post(new Runnable() {
    @Override
    public void run() {
      mList.setSelectionFromTop(index, top);
   }
});

ВЫ ДОЛЖНЫ ПОЗВОНИТЬ В ПОЧТУ И В РЕЗЬБЕ!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...