我有一个像这样的层次结构的应用程序:
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();
}
所以我有几个问题:
答案 0 :(得分:3)
我不确定如何在Mono中执行此操作,但是要将子片段添加到另一个片段,您无法使用FragmentManager
的{{1}}。相反,您必须使用托管Activity
的{{1}}:
http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager() http://developer.android.com/reference/android/support/v4/app/Fragment.html#getChildFragmentManager()
ChildFragmentManager
的主Fragment
处理您的标签
FragmentManager
的{{1}}处理拆分视图。
答案 1 :(得分: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的源代码,我可以看到它只是在切换选项卡时分离并重新附加选项卡的内容片段。似乎父Fragment的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);
}
现在,当切换标签和改变方向时,这一切看起来都很完美。