5

У меня есть приложение с иерархией, как это:Android FragmentTab хозяин и фрагменты внутри фрагментов

FragmentTabHost (Main Activity) 
    - Fragment (tab 1 content - splitter view) 
    - Fragment (lhs, list) 
    - Framment (rhs, content view) 
    - Fragment (tab 2 content) 
    - Fragment (tab 2 content) 

Все фрагмент мнения раздувается из ресурсов.

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

Рытье немного глубже, это то, что происходит:

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

Я работал над этим, удалив дочерние фрагменты из диспетчера фрагментов (я использую Моно), и теперь я может переключаться на вкладки без исключения.

public override void OnDestroyView() 
{ 
    var ft = FragmentManager.BeginTransaction(); 
    ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ListFragment)); 
    ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ContentFragment)); 
    ft.Commit(); 

    base.OnDestroyView(); 
} 

Поэтому у меня есть несколько вопросов:

  1. СОБРАННОЙ правильный способ сделать это?
  2. Если нет, как я должен это делать?
  3. В любом случае, как сохранение состояния экземпляра связывает все это, чтобы я не терял состояние отображения при переключении вкладок?

ответ

2

Я не знаю, как сделать это в моно, но добавлять дочерние фрагменты другого фрагмента, вы не можете использовать FragmentManager в Activity. Вместо этого, вы должны использовать ChildFragmentManager хостинговой Fragment:

http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager() http://developer.android.com/reference/android/support/v4/app/Fragment.html#getChildFragmentManager()

Основной FragmentManager из Activity ручки вкладки.
ChildFragmentManagertab1 обрабатывает разделенные виды.

+0

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

+0

Вы добавляете дочерний фрагмент внутри родительского фрагмента, используя код родительского фрагмента, используя ChildFragmentManager для добавления своих дочерних элементов. Затем надуватель, который использует ваш метод обратного вызова onCreateView вашего (дочернего) фрагмента: https://developer.android.com/reference/android/app/Fragment.html#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle). –

+0

Вам, вероятно, нужно сделать еще несколько поисковых запросов/stackoverlowing, чтобы посмотреть, как бороться с дочерними фрагментами в Mono. Я не могу вам помочь (я использую Java). –

2

ОК, я, наконец, понял это:

Как было отмечено выше, сначала я изменил создание фрагмента быть сделано программно, и что они добавили к менеджеру фрагмента ребенка, например, так:

public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) 
{ 
    var view = inflater.Inflate(Resource.Layout.MyView, viewGroup, false); 

    // Add fragments to the child fragment manager 
    // DONT DO THIS, SEE BELOW 
    var tx = ChildFragmentManager.BeginTransaction(); 
    tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); 
    tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); 
    tx.Commit(); 

    return view; 
} 

Как и ожидалось, каждый раз, когда я переключаю вкладки, будет создан дополнительный экземпляр Lhs/RhsFragment, но я заметил, что также будет вызван старый OnCreateView Lhs/RhsFragment. Поэтому после каждого переключения вкладки в OnCreateView будет еще один вызов. Переключение вкладок 10 раз = 11 вызовов OnCreateView. Это, очевидно, неправильно.

Рассматривая исходный код для FragmentTabHost, я вижу, что он просто отделяет и повторно прикрепляет фрагмент содержимого вкладки при переключении вкладок. Кажется, родительский Fragment's ChildFragmentManager поддерживает дочерние фрагменты и автоматически воссоздает их представления, когда родительский фрагмент повторно привязан.

Итак, я переместил создание фрагментов OnCreate, и только если мы не загружаются из сохраненного состояния:

public override void OnCreate(Bundle savedInstanceState) 
{ 
    base.OnCreate(savedInstanceState); 

    if (savedInstanceState == null) 
    { 
     var tx = ChildFragmentManager.BeginTransaction(); 
     tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); 
     tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); 
     tx.Commit(); 
    } 
} 


public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) 
{ 
    // Don't instatiate child fragments here 

    return inflater.Inflate(Resource.Layout.MyView, viewGroup, false); 
} 

Это фиксированное создание дополнительных представлений и вкладки переключения основно работает в настоящее время.

Следующий вопрос: сохранение и восстановление состояния просмотра. В дочерних фрагментах мне нужно сохранить и восстановить выбранный элемент. Изначально у меня было что-то вроде этого (это OnCreateView фрагмента дочернего)

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) 
{ 
    var view = inflater.Inflate(Resource.Layout.CentresList, container, false); 

    // ... other code ommitted ... 

    // DONT DO THIS, SEE BELOW 
    if (savedInstance != null) 
    { 
     // Restore selection 
     _selection = savedInstance.GetString(KEY_SELECTION); 
    } 
    else 
    { 
     // Select first item 
     _selection =_items[0]; 
    } 

    return view; 
} 

Проблема в том, что вкладка хоста не вызывает OnSaveInstanceState при переключении вкладок. Скорее, дочерний фрагмент остается в живых, а переменная _selection может быть просто оставлена ​​в покое.

Так я переехал код для управления выбором в OnCreate:

public override void OnCreate(Bundle savedInstance) 
{ 
    base.OnCreate(savedInstance); 

    if (savedInstance != null) 
    { 
     // Restore Selection 
     _selection = savedInstance.GetString(BK_SELECTION); 
    } 
    else 
    { 
     // Select first item 
     _selection = _items[0]; 
    } 
} 

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) 
{ 
    // Don't restore/init _selection here 

    return inflater.Inflate(Resource.Layout.CentresList, container, false); 
} 

Теперь все это, кажется, работает прекрасно, как при переключении вкладок и изменений ориентации.

Смежные вопросы