儿童片段被破坏没有很好的理由(Child fragment gets destroyed for n

2019-08-17 07:21发布

信息:我有一个2面板布局(2子Fragments一中) ParentFragment ,这当然是一个内部FragmentActivity 。 我有setRetainInstance(true)ParentFragment 。 在改变方向时子片段不被摧毁( onCreate()不会被调用),这是正常的(因为母公司保持其实例)。

问题:在方向变化, 正确的片段销毁( onCreate()被调用)。 凭啥是正确的片段破坏,左侧是不是?

编辑:如果我删除setRetainInstance(true) ,然后片段的onCreate()被调用两次 (笑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执行的唯一变化驻留内的父onCreateView() Fragment

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

Answer 1:

从我的理解,如果你有setRetainInstance(true)与上面的代码片段父,你的片段应该重新,但您的权利片段不应该,改变方向的时候。 这是倒退到你上面写的是什么,但我会解释为什么是这样的话反正。 如果你有setRetainInstance(false)父片段,你的确应该看到所创建的左片段两次被创建一次正确的片段。

案例1: setRetainInstance(true)

你的父母片段将不会在旋转被破坏。 但是,它仍然会重建自己的看法每次( onDestroyViewonCreateView将被调用,按照这个顺序)。 在onCreateView你的代码在一定条件下增加你的左边片段。 getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left)应该是非空的,因为一个片段加入到该容器之前。 getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout()应该是假的,因为只有通过XML添加片段会导致它返回true 。 总体情况是真实的,所以你的左片段的新实例将被创建,它会取代旧的。 您的权利片段的单击事件期间只实例化,所以没有特殊的行为发生。

摘要:家长的碎片遗物,创建新的左片段,右碎片遗物。

案例2: setRetainInstance(false)

你的父母片段被破坏,所以是左,右片段。 所有这三个片段由Android自动重新创建。 那么父片段将有机会来创建其视图,它会创建左片段的新实例,按照上面的说明。 刚刚生成的左片段将通过这个新的实例来代替。 你会发现,左片段将被销毁,另外左片段将被创建。 没有特殊的行为发生在合适的片段。

摘要:创建新的父片段,创建了两个新的左片段,创建新的正确的片段。

如果您是确保在setRetainInstance(true)的情况下,你的右手片段被破坏,而不是你的左边,请张贴样本项目,GitHub上的/ etc。 这说明了这一点。

更新:为什么正确的片段被删除,如果您使用FragmentTransaction.replace()左侧片段

由于内有条件的,你的代码将尝试在同一容器上本身来代替你的左边片段。

下面是从处理一个替代的Android 4.1源代码的代码片段:

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

如果您尝试替换本身相同的片段,有一些代码,试图忽略这个操作:

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

由于f为空,我们仍在继续通过我们的片段重复,这似乎已经移除从FragmentManager每个后续片段的副作用。 我不认为这是故意的,但至少是解释了为什么你的权利片段被破坏掉了。 不使用替换/不带自身更换相同片段可以解决你的问题。

有趣的是,这是一个最近的变化和旧版Android中并不存在。 https://github.com/android/platform_frameworks_support/commit/5506618c80a292ac275d8b0c1046b446c7f58836

错误报告: https://code.google.com/p/android/issues/detail?id=43265



文章来源: Child fragment gets destroyed for no good reason