понедельник, 19 января 2015 г.

Android. Save fragment after screen rotation

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

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

Причина


Activity во время своей жизни может множество раз менять свое состояние, вплоть до уничтожения и пересоздания. Это как раз и происходит во время поворота экрана. Activity уничтожается и создается снова.

Решение в лоб


Эта проблема довольно легко решается с помощью методов onSaveInstanceState и onRestoreInstanceState у Activity. В первом методе можно сохранить данные в виде пары Ключ/Значение, а во втором восстановить их. Эти данные будут сохранены после уничтожения Activity.

По данной теме можно найти множество материалов.

Однако, этот метод неуместен, если необходимо сохранять большое число данных.

Решение при использовании Fragments


Если форма с полями ввода находится во фрагменте, можно просто напросто сохранить фрагмент в памяти, и снова задействовать его после поворота экрана. Однако, нужно иметь ввиду, что фрагмент в любом случае пересоздаст свое представление, что необходимо обработать.

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

this.setRetainInstance(true);

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

private static String FRAGMENT_INSTANCE_NAME = "fragment";
MyFragment fragment = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_activity);

    // Восстанавливаем уже созданный фрагмент
    FragmentManager fm = getSupportFragmentManager();
    fragment = (MyFragment) fm.findFragmentByTag(FRAGMENT_INSTANCE_NAME);
    // Если фрагмент не сохранен, создаем новый экземпляр
    if(fragment == null){
        fragment = new MyFragment();
        fm.beginTransaction().add(R.id.container, fragment, FRAGMENT_INSTANCE_NAME).commit();
    }
}

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

View view = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Проверяем, создано ли представление фрагмента
    if(view == null) {
        // Если представления нет, создаем его
        view = inflater.inflate(R.layout.my_fragment, container, false);
    } else {
        // Если представление есть, удаляем его из разметки,
        // иначе возникнет ошибка при его добавлении
        ((ViewGroup) view.getParent()).removeView(view);
    }
    return view;
}

Все готово. При повороте экрана Activity использует старый экземпляр фрагмента, представление которого так же не пересоздается. Это позволяет сохранить все введенные в форме данные.

Внимание


Если вы используете адаптеры, то при повороте экрана они автоматически уничтожат и создадут заново каждый элемент, за который они отвечают. Это произойдет с вызовом метода getView.



Комментариев нет:

Отправить комментарий