Как сохранить положение прокрутки в ExpandableListView

В моем приложении у меня есть класс, полученный из ExpandableListActivity. Когда я просматриваю содержимое, а затем меняю ориентацию телефона или редактирую элемент, а затем возвращаюсь к списку, позиция исходного списка не сохраняется. Я попробовал два решения, аналогичные тем, которые я недавно использовал с классами, созданными в ListActivity.

Решение 1:

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

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

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

Решение 2:

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    mListPosition = state.getInt(LIST_STATE);
}

protected void onResume() {
    super.onResume();
    loadData();
    getExpandableListView().setSelection(mListPosition);
}

protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    mListPosition = getExpandableListView().getFirstVisiblePosition();
    state.putInt(LIST_STATE, mListPosition);
}

Ни одно из решений не работает. Я также попытался объединить два решения, и это работает, когда я редактирую элемент и возвращаюсь к списку, но он НЕ работает, когда я меняю ориентацию телефона

Кто-нибудь может предложить хороший способ достичь моей цели?

Ответы

Ответ 1

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

На самом деле необходимо сохранить и восстановить три разных фрагмента информации: состояние списка (например, какие группы расширены), индекс первого видимого элемента и его точное положение прокрутки.

К сожалению, кажется, что только первый из них сохраняется методом onSaveInstanceState в расширяемом виде списка, поэтому остальные два необходимо сохранить отдельно. Это отличается от нерасширяемого вида списка, где он появляется, метод onSaveInstanceState сохраняет всю информацию, необходимую для правильного восстановления состояния и положения списка (по этому вопросу см. Сохранить/Сохранить/Восстановить позицию прокрутки при возврате в ListView).

Вот фрагменты кода; поверх класса ExpandableListActivity:

private static final String LIST_STATE_KEY = "listState";
private static final String LIST_POSITION_KEY = "listPosition";
private static final String ITEM_POSITION_KEY = "itemPosition";

private Parcelable mListState = null;
private int mListPosition = 0;
private int mItemPosition = 0;

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

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);

    // Retrieve list state and list/item positions
    mListState = state.getParcelable(LIST_STATE_KEY);
    mListPosition = state.getInt(LIST_POSITION_KEY);
    mItemPosition = state.getInt(ITEM_POSITION_KEY);
}

protected void onResume() {
    super.onResume();

    // Load data from DB and put it onto the list
    loadData();

    // Restore list state and list/item positions
    ExpandableListView listView = getExpandableListView();
    if (mListState != null)
        listView.onRestoreInstanceState(mListState);
    listView.setSelectionFromTop(mListPosition, mItemPosition);
}

protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);

    // Save list state
    ExpandableListView listView = getExpandableListView();
    mListState = listView.onSaveInstanceState();
    state.putParcelable(LIST_STATE_KEY, mListState);

    // Save position of first visible item
    mListPosition = listView.getFirstVisiblePosition();
    state.putInt(LIST_POSITION_KEY, mListPosition);

    // Save scroll position of item
    View itemView = listView.getChildAt(0);
    mItemPosition = itemView == null ? 0 : itemView.getTop();
    state.putInt(ITEM_POSITION_KEY, mItemPosition);
}

Это отлично работает на моем устройстве Froyo.

Ответ 2

Вот как я получил ExpandableListView.onRestoreInstanceState и onSaveInstanceState для работы...

Если вы используете BaseExpandableListAdapter, реализация по умолчанию getCombinedChildId возвращает отрицательное число. Если вы посмотрите на код для восстановления списка из сохраненной позиции прокрутки состояния, он игнорирует иды, которые являются отрицательными. Верните положительные идентификаторы, чтобы AbsListView onRestoreInstanceState правильно установил положение прокрутки. Чтобы вернуть положительные идентификаторы, измените значение или getCombinedChildId с 0x8000000000000000L (отрицательный) на 0x7000000000000000L (положительный), переместив метод в вашем подклассе BaseExpandableListAdapter следующим образом:

        public long getCombinedChildId(long groupId, long childId)
        {
            long or  = 0x7000000000000000L;
            long group = (groupId & 0x7FFFFFFF) << 32;
            long child = childId & 0xFFFFFFFF;
            return or | group | child;
        }

Ответ 3

Я нашел другой способ сделать это для меня, чтобы поддерживать состояние. Вот ссылка.

http://markmail.org/message/msesxums77bdoqnx#query:+page:1+mid:utmart4ob7flh3u7+state:results

Резюме состоит в том, что вам всегда нужно установить состояние, даже если convertView!= null.

Я не знаю последствий всегда установки состояния, но в моем случае он работает отлично.

Ответ 4

Чтобы предотвратить перезагрузку вашей деятельности при изменении ориентации телефона, просто добавьте следующую строку в свой AndroidManifest.xml в разделе действий:

        android:configChanges="keyboardHidden|orientation|screenSize"