儿童片段被破坏没有充分的理由

时间:2013-03-04 14:24:49

标签: android android-fragments android-fragmentactivity android-nested-fragment

信息:我在Fragments内有2个窗格布局(2个孩子ParentFragment),当然,它位于FragmentActivity内。我在setRetainInstance(true)上有ParentFragment。在方向更改时,子片段未被销毁(onCreate()未被调用),这是正常的(因为父母保留其实例)。

问题:在方向更改时,片段获取销毁(onCreate()被调用)。为什么地狱是正确的片段被破坏而左边的片段不是?

编辑:如果我删除setRetainInstance(true),那么片段的onCreate()会被称为两次(lol wtf并且片段的onCreate()被调用一次。所以这也不好......

以下代码为ParentFragment:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    this.setRetainInstance(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.fragment_schedule, container, false);
    setHasOptionsMenu(true);


    if (getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left) == null || 
            !getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout())
    {
        if (mPresentationsListFragment == null)
            mPresentationsListFragment = PresentationsListFragment.newInstance(PresentationsListFragment.TYPE_SCHEDULE, mScheduleDate);
        getChildFragmentManager().beginTransaction()
                                     .replace(R.id.fragment_schedule_framelayout_left, mPresentationsListFragment)
                                     .commit();
    }
    mPresentationsListFragment.setOnPresentationClickListener(this);


    return view;
}


@Override
    public void onPresentationClick(int id)
    {
        if (Application.isDeviceTablet(getActivity()))
        {
            if (getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_right) == null)
            {
                if (mPresentationDetailFragment == null)
                    mPresentationDetailFragment = PresentationDetailFragment.newInstance(id);
                else
                    mPresentationDetailFragment.loadPresentation(id);
                getChildFragmentManager().beginTransaction()
                                           .replace(R.id.fragment_schedule_framelayout_right, mPresentationDetailFragment)
                                           .commit();
            }
            else
                mPresentationDetailFragment.loadPresentation(id);
        }
        else
        {
            Intent presentationDetailIntent = new Intent(getActivity(), PresentationDetailActivity.class);
            presentationDetailIntent.putExtra(PresentationDetailActivity.KEY_PRESENTATION_ID, id);
            startActivity(presentationDetailIntent);
        }
    }

LE解决方案: 非常感谢 antonyt ,答案如下。执行pe所需的唯一更改位于父Fragment的onCreateView()内。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.fragment_schedule, container, false);
    setHasOptionsMenu(true);


    if (getChildFragmentManager().findFragmentById(R.id.fragment_presentations_framelayout_left) == null)
    {
        mPresentationsListFragment = PresentationsListFragment.newInstance();
        mPresentationsListFragment.setOnPresentationClickListener(this);
        getChildFragmentManager().beginTransaction()
                .add(R.id.fragment_presentations_framelayout_left, mPresentationsListFragment)
                .commit();
    }


    return view;
}

1 个答案:

答案 0 :(得分:5)

根据我的理解,如果您使用上述代码在父片段上setRetainInstance(true),则应重新创建片段,但正确片段应该改变方向时不要这样。这与你上面写的内容相反,但我会解释为什么会出现这种情况。如果你在父片段上有setRetainInstance(false),你确实应该看到左片段被创建两次而右片段被创建一次。

案例1:setRetainInstance(true)

您的父片段在轮换时不会被销毁。但是,每次都会重新创建其视图(按此顺序调用onDestroyViewonCreateView)。在onCreateView中,您有代码可以在特定条件下添加左侧片段。 getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left)应为非null,因为之前已将片段添加到该容器中。 getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout()以来setRetainInstance(false)应该是假的。总体情况是正确的,因此将创建左侧片段的新实例,它将替换旧的实例。您的正确片段仅在单击事件期间实例化,因此不会发生特殊行为。

总结:父片段仍然存在,新的左片段被创建,右片段仍然存在。

案例2:setRetainInstance(true)

您的父片段被破坏,左右片段也被破坏。所有三个片段都由Android自动重建。然后,您的父片段将有机会创建其视图,并将根据上述说明创建左片段的新实例。刚刚创建的左侧片段将被此新实例替换。您将观察到左侧片段将被销毁,另一个左侧片段将被创建。正确的片段没有特殊的行为。

总结:创建了新的父片段,创建了两个新的左片段,创建了新的右片段。

如果您确定在FragmentTransaction.replace()案例中,您的正确片段正在销毁而不是您的左片段,请将示例项目发布到github / etc。这证明了这一点。

更新:如果您在左侧片段中使用... case OP_REPLACE: { Fragment f = op.fragment; if (mManager.mAdded != null) { for (int i=0; i<mManager.mAdded.size(); i++) { Fragment old = mManager.mAdded.get(i); if (FragmentManagerImpl.DEBUG) Log.v(TAG, "OP_REPLACE: adding=" + f + " old=" + old); if (f == null || old.mContainerId == f.mContainerId) { if (old == f) { op.fragment = f = null; } else { if (op.removed == null) { op.removed = new ArrayList<Fragment>(); } op.removed.add(old); old.mNextAnim = op.exitAnim; if (mAddToBackStack) { old.mBackStackNesting += 1; if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " + old + " to " + old.mBackStackNesting); } mManager.removeFragment(old, mTransition, mTransitionStyle); } } } } if (f != null) { f.mNextAnim = op.enterAnim; mManager.addFragment(f, false); } } break; ... ,为什么会删除正确的片段

由于内部条件,您的代码将尝试在同一容器上用自己替换左侧片段。

以下是处理替换的Android 4.1源代码的代码段:

if (old == f) {
    op.fragment = f = null;
}

如果您尝试用自己替换相同的片段,则有一些代码可以尝试忽略此操作:

f

由于{{1}}为空,并且我们仍在继续迭代我们的片段,这似乎具有从FragmentManager中删除每个后续片段的副作用。我不认为这是故意的,但至少可以解释为什么你的正确片段会被破坏。不使用替换/不替换相同的片段可以解决您的问题。

有趣的是,这是最近的一次更改,并且在先前版本的Android中不存在。 only fragments added via XML will cause it to return true

错误报告:https://github.com/android/platform_frameworks_support/commit/5506618c80a292ac275d8b0c1046b446c7f58836