Фрагмент - следует ли повторно использовать представление в onCreateView и как мне это сделать?

На самом деле, я всегда использовал свое представление в своих фрагментах, как показано ниже:

private View mView = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    if (mView == null)
        mView = inflater.inflate(R.layout.view);
    return mView;
}

Это сработало с помощью viewpager и так далее. Теперь я начал использовать свои фрагменты в простых действиях, и если и только если я добавлю фрагмент в backstack, это приведет к сбою из-за java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child parent first.

Итак, мои вопросы:

  • Все в порядке, если я проверяю родительские представления, удаляю его и добавляю в новый родитель?
  • Или я должен всегда воссоздавать представление и не использовать его повторно? Если да, то почему?
  • Существуют ли другие точки, в которых повторное использование представления не будет выполнено?

Ответ 1

Возможно, это поможет понять поведение. Если вы посмотрите FragmentManagerImpl.java, вы найдете следующее:

Сначала мы создаем представление, вызывая onCreateView() (строка 845), а затем мы завершаем созданный вид другим представлением, которое становится родителем нашего представления (строки 848-849). Это означает, что наше представление не становится дочерним элементом реального контейнера, но теперь оно представляет собой дочерний элемент оболочки. Проблема с повторным использованием происходит, когда вид удаляется из контейнера (строка 998). FragmentManager удаляет вид контейнера из контейнера, но наш реальный вид остается добавленным к представлению родительской оболочки. Именно это и вызывает проблему.

Таким образом, если вы удалите представление из своего родителя, он может работать. Даже зная это, я бы не рекомендовал повторно использовать представления в фрагменте, так как представления могут жить немного дольше, чем фрагменты, потому что они могут использоваться в "исчезающих" анимациях даже после уничтожения фрагмента. Если вы попытаетесь удалить такое представление из своего родителя в это время, анимация может быть нарушена.

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

Ответ 2

В настоящее время я повторно использую представление с чем-то вроде этого:

if(view == null){
    view = (ViewGroup) inflater.inflate(R.layout.news_list, container, false);
} else {
    ((ViewGroup) view.getParent()).removeView(view);
}
return view;

Я не знаю, правильно ли этот путь, но он работает для меня.

ПРИМЕЧАНИЕ. Я использую этот aproach, потому что у меня есть список в фрагменте, и когда пользователь нажимает на элемент, он загружает новый фрагмент (менеджер фрагментов заменяет текущий фрагмент списка). Затем, когда пользователь нажимает на кнопку, поскольку я повторно использую то же самое старое представление об фрагменте (которое не уничтожается при удалении с помощью FM), пользователь продолжает просмотр списка в позиции, которая была перед открытием фрагмента фрагмента.