Подтвердить что ты не робот

Хорошее решение для сохранения элементов списка, когда пользователь поворачивает телефон и сохраняет все данные в ArrayAdapter

Я использую Fragment с listView. Я заполняю ArrayAdapter, связанный с этим списком, данными, полученными в пользовательском загрузчике (из Интернета). Пользовательский ArrayAdapter поддерживает бесконечную прокрутку (paging).

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

Я собираюсь создать невизуальный фрагмент с ArrayAdapter и использовать метод setRetainInstance для сохранения значений.

Любые предложения для лучшего решения?

4b9b3361

Ответ 1

Чтобы работать с фреймворком Android и жизненным циклом фрагмента, вы должны реализовать метод onSaveInstanceState в своем фрагменте. Для простоты я предположил, что у вас есть массив значений String, к которым вы можете обратиться (я обычно расширяю ArrayAdapter, чтобы инкапсулировать конструкцию представления и предоставить удобный метод для доступа ко всему базовому набору данных):

public void onSaveInstanceState(Bundle savedState) {

    super.onSaveInstanceState(savedState);

    // Note: getValues() is a method in your ArrayAdapter subclass
    String[] values = mAdapter.getValues(); 
    savedState.putStringArray("myKey", values);

}

Затем вы можете извлечь данные в свой метод onCreate (или onCreateView или onActivityCreated - см. фрагмент JavaDoc) следующим образом:

public void onCreate (Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        String[] values = savedInstanceState.getStringArray("myKey");
        if (values != null) {
           mAdapter = new MyAdapter(values);
        }
    }

    ...

}

Это гарантирует, что все события жизненного цикла будут обрабатываться надлежащим образом, без потери данных, включая поворот устройства и переход пользователя в другие приложения. Опасность использования onSaveInstanceState и использования памяти - это опасность, что Android восстановит эту память. Сохраненное состояние не будет затронуто этим, но использование переменных экземпляра или скрытых фрагментов приведет к потере данных.

Если savedStateInstance имеет значение null, то нет состояния для восстановления.

if (values != null) просто защищает от возможности сохранения массива, но если вы закодируете ArrayAdapter для обработки нулевого набора данных, вам это не понадобится.

Окончательное решение, если ваши строки являются экземплярами одного из ваших собственных классов, а не отдельных элементов данных, заключается в реализации интерфейса Parcelable в этом классе, тогда вы можете использовать savedState.putParcelableArray("myKey", myArray). Вы были бы удивлены, насколько полезно знать, как реализовать Parcelable - он позволяет вам проходить ваши классы по сути и позволяет писать гораздо более чистый код.

Ответ 2

Когда устройство повернуто, приложение перезапускается. Поэтому onSaveInstance вызывается до того, как приложение будет уничтожено. Вы можете сохранить адаптер массива в onSaveInstance, а когда onCreate наконец будет вызван, когда приложение будет запущено снова, вы можете получить адаптер массива и установить его в виде списка.

Ответ 3

Очень легко сохранить элементы вашего адаптера в onSaveInstance.

Теперь вам нужно сохранить местоположение (Scroll). Вы можете сделать это

простой способ

Поскольку изменение screenOrientation все равно повредит, вы можете разрешить некоторую предельную ошибку и просто в своем onSaveInstance сохранить первый видимый элемент с помощью listView.getFirstVisiblePosition().

Затем в onRestoreInstance вы можете получить этот индекс для прокрутки к одному элементу с помощью listview.setSelection(position). Конечно, вы можете использовать listview.smoothScrollToPosition(position), но это было бы странно.


трудный путь

Чтобы иметь более точную позицию, вам нужно будет спуститься на еще один уровень: получите позицию прокрутки (не индекс) и сохраните эту позицию. Затем, после восстановления, прокрутите назад до этой позиции. В onSaveInstance сохраните позицию, возвращенную с listview.getScrollY(). Когда вы вернете это положение в onRestoreInstance, вы можете вернуться к этой позиции с помощью listview.scrollTo(0, position).


Почему это действительно сложно?

Simple. Вероятно, вы, скорее всего, не сможете вернуться к этой позиции, потому что ваш список пока не пройдет измерение и макет. Вы можете преодолеть это, ожидая макета после установки адаптера с помощью getViewObserver().addOnGlobalLayoutListener. В этом обратном вызове отправьте runnable, чтобы перейти к позиции.


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

Ответ 4

когда ориентация изменяет вызовы жизненного цикла активности onPause, а затем onRestart Сделайте что-то вроде этого -

@Override
protected void onRestart() {
    super.onRestart();
    mAdapter.getFilter().filter(getFilterSettings());
    mAdapter.notifyDataSetChanged();
}

Ответ 5

Я не знаю содержимое вашего ArrayAdapter или List, поддерживающего его, но как сделать резервную копию данных сериализуемым списком, сохранить его, а затем загрузить его при воссоздании представления?

Традиционно я видел эту оценку, используемую, когда вы пытаетесь хранить данные, когда приложение закрывается или рискует быть убитым из оставшихся в фоновом режиме. Существует большой пост сериализации в комплекте с примером кода здесь. Они проходят через ArrayList пользовательских объектов, записывая их в файл и повторно открывая его позже. Я думаю, что если бы вы применили этот подход, записав данные вашей поддержки List или ArrayAdapter в onPause() до того, как действие будет уничтожено, вы сможете перезагрузить этот файл, когда активируется активация.

Другие подходы (планы резервного копирования a/k/a):

(1) Легко, но неаккуратно. Если ваш список является некоторым примитивным, например списком строк, вы всегда можете рассмотреть возможность записи значений в SharedPreferences и вернуть их при перезагрузке. Просто не забудьте указать уникальный идентификатор в процессе хранения.

ПРИМЕЧАНИЕ, хотя это может сработать, SharedPreferecnes обычно не предназначен для обработки больших объемов данных, поэтому, если список длинный, я бы избегал такого подхода. Однако для нескольких точек данных я не вижу в этом проблемы.

(2) Немного сложнее, но рискованно. Если вы поддерживаете List, адаптер содержит объекты, которые реализуют parcelable или уже сериализуемы, рассмотрите возможность передачи List через Intent в существующую активность, которая в фоновом режиме и используя обратный вызов для извлечения этих данных при воссоздании активности. Это похоже на вашу идею создания фона Fragment. Оба подхода сопряжены с риском того, что таргетинг Activity или Fragment не будет.

Ответ 6

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

class YourData{
public static List<YourDataObject> listViewData;
}

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

 @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       //Run the process to get data from database
      YourData.listViewData = dataRetrievedFromDatabase;
    } 

а затем в методе onResume обновите эти значения адаптеру примерно так:

@Override
    public void onResume() {
        super.onResume();
       if(YourData.listViewData!=null){
          yourListAdapater.setData = YourData.listViewData;
          listView.setAdapter(yourListAdapater);
        }else{
      //run your method to get data from server 
         }
    }

это можно использовать для сохранения любых данных за один сеанс