13

Actually, I always reused my view in my fragments like the following:

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;
}

That worked, with viewpager and so on. Now I started using my fragments in simple activities as well and if, and only if, I add the fragment to the backstack, this will fail because of java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

So my questions are:

  • Is it ok, if I check the the views parent, remove it and add it to the new parent?
  • Or should I always recreate the view and never reuse it? If yes, why?
  • Are there other points, where reusing the view will fail?
4

3 回答 3

14

也许这可以帮助理解行为。如果您查看FragmentManagerImpl.java,您会发现以下内容:

首先,我们通过调用onCreateView()第 845 行)创建一个视图,然后我们用另一个视图包装创建的视图,该视图成为我们视图的父视图(第 848-849 行)。这意味着我们的视图不会成为真正容器的孩子,但它现在是包装视图的孩子。当视图从容器中移除时(第 998 行),重用问题就会发生。FragmentManager 从容器中移除包装视图,但我们的真实视图仍然添加到父包装视图。这就是导致您遇到问题的原因。

因此,如果您从其父视图中删除视图,它就可以工作。即使知道这一点,我也不建议在片段中重用视图,因为视图可以比片段更长寿,因为即使在片段被销毁后它们也可以用于“消失”动画。如果您当时尝试从其父视图中移除此类视图,则动画可能会被破坏。

不缓存视图的另一个论点是,Android 不支持在设计的片段中回收视图。还记得ListAdapter允许重用视图吗?Android 负责缓存和正确重用这些视图。但是片段不是这种情况。

于 2013-09-05T14:36:32.190 回答
7

我目前正在重用视图,如下所示:

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

我不知道这种方式是否正确,但它似乎对我有用..

注意:我使用这种方法是因为我在片段中有一个列表视图,当用户点击一个项目时,它会加载一个新片段(片段管理器使用当前列表片段替换)。然后,当用户点击后退按钮时,因为我正在重用片段的相同旧视图(使用 FM 删除时不会被破坏),然后用户继续在打开详细片段视图之前的位置查看列表。

于 2014-05-07T13:31:36.963 回答
0

我知道这是一个老问题。但是在使用 Fragment 几个月后,我发现在这种方式使用缓存时需要提到的一点是:如果您当前缓存的布局还有另一个 Fragment 标签,这种缓存策略会导致嵌入 Fragment 丢失一些生命周期回调。我详细说一下:

1.当前片段onCreateView被第一次调用。通过像上面这样的缓存,我们将膨胀目标布局(这个布局包括一个片段标签)。2.inflater.inflate这将使嵌入片段正确添加到布局中,onCreateView将被调用。3.当前fragment需要销毁时,embed fragmentonDestroyView会被正确调用。

4.当当前fragmentonCreateView再次调用时,我们返回一个缓存视图,不调用inflater.inflate. 您会发现嵌入片段onCreateViewonDestroyView/或其他生命周期方法不会被调用。

这就是我想说的。

于 2018-05-10T03:46:53.700 回答