儿童的片段被破坏没有很好的理由 [英] Child fragment gets destroyed for no good reason

查看:177
本文介绍了儿童的片段被破坏没有很好的理由的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介:我有一个2面板布局(2子片段)内的 ParentFragment ,这当然是一个 FragmentActivity 里面。我有 setRetainInstance(真) ParentFragment 。在方向变化,在离开子片段没有得到破坏(<$ C C $>的onCreate()不会被调用) ,它是(因为父保留其实例)正常

问题:在方向改变,在右侧片段获得破坏(的onCreate()被调用)。凭啥是正确的片段销毁,留下的是不是?

编辑:如果我删除 setRetainInstance(真),然后在离开片段的的onCreate()被调用的两次(笑WTF),并在右侧片段的的onCreate()被调用一次。因此,这也不好......

下面的ParentFragment code:

  @覆盖
公共无效的onCreate(包savedInstanceState)
{
    super.onCreate(savedInstanceState);
    this.setRetainInstance(真正的);
}

@覆盖
公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,
        捆绑savedInstanceState)
{
    查看查看= inflater.inflate(R.layout.fragment_schedule,集装箱,假);
    setHasOptionsMenu(真正的);


    如果(getChildFragmentManager()。findFragmentById(R.id.fragment_schedule_framelayout_left)== NULL ||
            !getChildFragmentManager()。findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout())
    {
        如果(M presentationsListFragment == NULL)
            米presentationsListFragment = presentationsListFragment.newInstance(presentationsListFragment.TYPE_SCHEDULE,mScheduleDate);
        getChildFragmentManager()的BeginTransaction()
                                     .replace(R.id.fragment_schedule_framelayout_left中,M presentationsListFragment)
                                     。承诺();
    }
    米presentationsListFragment.setOn presentationClickListener(本);


    返回查看;
}


@覆盖
    公共无效于presentationClick(INT ID)
    {
        如果(Application.isDeviceTablet(getActivity()))
        {
            如果(getChildFragmentManager()。findFragmentById(R.id.fragment_schedule_framelayout_right)== NULL)
            {
                如果(M presentationDetailFragment == NULL)
                    米presentationDetailFragment = presentationDetailFragment.newInstance(ID);
                其他
                    米presentationDetailFragment.load presentation(ID);
                getChildFragmentManager()的BeginTransaction()
                                           .replace(R.id.fragment_schedule_framelayout_right中,M presentationDetailFragment)
                                           。承诺();
            }
            其他
                米presentationDetailFragment.load presentation(ID);
        }
        其他
        {
            意图presentationDetailIntent =新的意图(getActivity(),presentationDetailActivity.class);
            presentationDetailIntent.putExtra(presentationDetailActivity.KEY_ preSENTATION_ID,身份证);
            startActivity(presentationDetailIntent);
        }
    }
 

LE解决方法: 非常感谢为 antonyt ,答案是如下。需要PE进行的唯一更改位于内部的母公司onCreateView()片段

  @覆盖
公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,
        捆绑savedInstanceState)
{
    查看查看= inflater.inflate(R.layout.fragment_schedule,集装箱,假);
    setHasOptionsMenu(真正的);


    如果(getChildFragmentManager()。findFragmentById(R.id.fragment_ presentations_framelayout_left)== NULL)
    {
        米presentationsListFragment = presentationsListFragment.newInstance();
        米presentationsListFragment.setOn presentationClickListener(本);
        getChildFragmentManager()的BeginTransaction()
                。新增(R.id.fragment_ presentations_framelayout_left,男presentationsListFragment)
                。承诺();
    }


    返回查看;
}
 

解决方案

据我所知,如果你有 setRetainInstance(真)父片段与上述$上C $ C,您的离开片段应该重新但您的右侧片段不应该是,改变方向的时候。这是倒退到你上面写的是什么,但我会解释为什么是这样呢。如果你有 setRetainInstance(假)父片段上,你确实应该看到左边的片段被创建两次和正确的片段被创建一次。

案例1: setRetainInstance(真)

您父片段将不会被轮换销毁。然而,它仍然会在每次重新创建自己的观点( onDestroyView onCreateView 将被调用,按照这个顺序)。在 onCreateView 你有code在特定条件下加入你的左片段。 getChildFragmentManager()。findFragmentById(R.id.fragment_schedule_framelayout_left)应非空,因为一个片段添加到容器previously。 <$c$c>getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout()应该是假的,因为只能通过添加XML片段将导致其返回true 。总的情况是真实的,所以你的左片段的一个新的实例将被创建,它会取代旧的。你的右片段中点击事件仅实例化,所以没有特殊的行为发生。

摘要:家长片段仍然存在,将创建新的左段,右段仍然

案例2: setRetainInstance(假)

您的父片段被破坏,因此是在左,右片段。所有这三个片段是由机器人自动重新创建。然后,您的父母片段将有机会创建视图,并会造成左片段的新实例,按照上面的说明。刚刚创建的左片段将这个新的实例所取代。你会发现,左片段将被销毁,另外左片段将被创建。没有特别的行为发生在合适的片段。

摘要:在创建新的父片段,创建了两个新的左片段,新的右片段创建

如果您确信在 setRetainInstance(真)情况下,你的右片段被破坏,而不是你的左边,请张贴一个示例项目github上的/ etc 。这说明了这一点。

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

的内部条件因为,你的code将尝试用自身替换您的左边片段在同一容器中。

下面是从Android 4.1源$ C ​​$ C处理该code段一个替换:

  ...
案例OP_REPLACE:{
    片f = op.fragment;
    如果(mManager.mAdded!= NULL){
        的for(int i = 0; I&LT; mManager.mAdded.size();我++){
            片段旧= mManager.mAdded.get(我);
            如果(FragmentManagerImpl.DEBUG)Log.v(TAG,
                    OP_REPLACE:加=+ F +旧=+老);
            如果(F == NULL || old.mContainerId == f.mContainerId){
                如果(旧== F){
                    op.fragment = F = NULL;
                } 其他 {
                    如果(op.removed == NULL){
                        op.removed =新的ArrayList&LT;片断&gt;();
                    }
                    op.removed.add(旧);
                    old.mNextAnim = op.exitAnim;
                    如果(mAddToBackStack){
                        old.mBackStackNesting + = 1;
                        如果(FragmentManagerImpl.DEBUG)Log.v(TAG,凹凸嵌套
                                +老+到+ old.mBackStackNesting);
                    }
                    mManager.removeFragment(老,mTransition,mTransitionStyle);
                }
            }
        }
    }
    如果(F!= NULL){
        f.mNextAnim = op.enterAnim;
        mManager.addFragment(F,假);
    }
} 打破;
...
 

如果您尝试更换相同的片段,本身就存在一些code,试图忽略这个操作:

 如果(旧== F){
    op.fragment = F = NULL;
}
 

由于 F 为空,而且我们还在不断遍历我们的碎片,这似乎已经删除每一个从FragmentManager后续片段的副​​作用。我不认为这是故意的,但最起码​​解释了为什么你的权利片段被破坏掉了。不使用替代/不与自己更换相同的片段可以解决你的问题。

有趣的是,这是一个最近的变化和Android系统previous版本并不存在。 <一href="https://github.com/android/platform_frameworks_support/commit/5506618c80a292ac275d8b0c1046b446c7f58836" rel="nofollow">https://github.com/android/platform_frameworks_support/commit/5506618c80a292ac275d8b0c1046b446c7f58836

错误报告: https://开头code .google.com / P /安卓/问题/详细信息?ID = 43265

Info: I have a 2 pane layout (2 child Fragments) inside a ParentFragment, which, of course, is inside a FragmentActivity. I have setRetainInstance(true) on the ParentFragment. On orientation change, the left child fragment doesn't get destroyed (onCreate() doesn't get called), which is normal (because of the parent retaining its instance).

Problem: On orientation change, the right fragment gets destroyed (onCreate() gets called). Why the hell is the right fragment destroyed and the left one isn't ?

EDIT: If I remove setRetainInstance(true), then the left fragment's onCreate() gets called twice (lol wtf) and the right fragment's onCreate() gets called once. So this isn't good either...

Code below for the 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 Solution: Thanks a lot to antonyt , the answer is below. The only changes needed to pe performed reside inside onCreateView() of the parent 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;
}

解决方案

From what I understand, if you have setRetainInstance(true) on the parent fragment with the above code, your left fragment should be recreated but your right fragment should not be, when changing orientation. This is backwards to what you wrote above, but I will explain why this is the case anyway. If you have setRetainInstance(false) on the parent fragment, you indeed should see the left fragment being created twice and the right fragment being created once.

Case 1: setRetainInstance(true)

Your parent fragment will not be destroyed on rotation. However, it will still recreate its views each time (onDestroyView and onCreateView will be called, in that order). In onCreateView you have code to add your left fragment under certain conditions. getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left) should be non-null, since a fragment was added to that container previously. getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout() should be false since only fragments added via XML will cause it to return true. The overall condition is true and so a new instance of your left fragment will be created and it will replace the old one. Your right fragment is only instantiated during a click event and so no special behavior happens.

Summary: Parent fragment remains, new left fragment is created, right fragment remains.

Case 2: setRetainInstance(false)

Your parent fragment is destroyed, and so are the left and right fragments. All three fragments are recreated automatically by Android. Your parent fragment will then get a chance to create its view, and it will create a new instance of the left fragment as per the explanation above. The just-created left fragment will be replaced by this new instance. You will observe that a left fragment will be destroyed and another left fragment will be created. No special behavior happens for the right fragment.

Summary: New parent fragment is created, two new left fragments are created, new right fragment is created.

If you are sure that in the setRetainInstance(true) case, your right fragment is being destroyed and not your left one, please post a sample project to github/etc. that demonstrates this.

Update: Why the right fragment gets removed if you use FragmentTransaction.replace() on the left fragment

Because of the inner conditional, your code will try to replace your left fragment with itself on the same container.

Here is the code snippet from the Android 4.1 source code that handles a replace:

...
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 you try to replace the same fragment with itself, there is some code to try and ignore this operation:

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

Since f is null, and we are still continuing to iterate through our fragments, this seems to have the side effect of removing every subsequent fragment from the FragmentManager. I don't think this is intentional, but at the very least explains why your right fragment is getting destroyed. Not using replace / not replacing the same fragment with itself can fix your issues.

Interestingly, this was a recent change and did not exist in previous versions of Android. https://github.com/android/platform_frameworks_support/commit/5506618c80a292ac275d8b0c1046b446c7f58836

Bug report: https://code.google.com/p/android/issues/detail?id=43265

这篇关于儿童的片段被破坏没有很好的理由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆