6

我有一个具有如下层次结构的应用程序:

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 的视图。

再深入一点,这就是正在发生的事情:

  • 在第一次加载时,扩展拆分器视图会导致其两个子片段被添加到片段管理器中。
  • 从第一个选项卡切换时,它的视图被破坏,但它的子片段留在片段管理器中
  • 在切换回第一个选项卡时,视图会重新膨胀,并且由于旧的子片段仍在片段管理器中,因此在实例化新的子片段时会引发异常(通过膨胀)

我通过从片段管理器中删除子片段(我正在使用 Mono)解决了这个问题,现在我可以毫无例外地切换选项卡。

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. 无论哪种方式,保存实例状态如何与所有这些相关联,以便在切换选项卡时不会丢失视图状态?
4

2 回答 2

3

我不确定如何在 Mono 中执行此操作,但是要将子片段添加到另一个片段,您不能使用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()

主要处理您FragmentManagerActivity选项卡。of处理拆分视图
ChildFragmentManagertab1

于 2013-03-11T23:19:35.473 回答
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 实例,但我注意到旧的 Lhs/RhsFragment 的 OnCreateView 也会被调用。因此,在每次切换选项卡之后,都会再调用一次 OnCreateView。切换选项卡 10 次 = 11 次调用 OnCreateView。这显然是错误的。

查看 FragmentTabHost 的源代码,我可以看到它只是在切换选项卡时分离并重新附加选项卡的内容片段。似乎父片段的 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);
}

现在,无论是切换选项卡还是更改方向,这一切似乎都运行良好。

于 2013-03-14T23:02:01.287 回答