Android 在已创建父片段视图后添加嵌套片段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20041411/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:12:02  来源:igfitidea点击:

Add Nested Fragment after Parent Fragment view is already created

androidandroid-fragmentsnested

提问by Sound Conception

I am trying to create a Fragment which has a public method for adding child Fragments to itself.

我正在尝试创建一个 Fragment,它具有将子 Fragment 添加到自身的公共方法。

I've been reading through potentially similar questions but haven't found anything to help so far. I've reduced the problem down to a simple test app shown below.

我一直在阅读可能类似的问题,但到目前为止还没有找到任何帮助。我已将问题简化为如下所示的简单测试应用程序。

Once fragAhas been added to the main layout, I call the public method fragA.addFragB()to get it to add an instance of FragmentClassB to itself, but this causes the test app to crash, indicating "Activity has been destroyed" (see LogCat at the end of the post). Does this mean fragAhas been destroyed so I can't add fragBto it, or does it mean fragBhas been destroyed so I can't add it to fragA? Or does it mean something else entirely?

一旦fragA已被添加到主布局,我呼吁公众方法fragA.addFragB()得到它FragmentClassB的一个实例添加到本身,而是这会导致测试应用崩溃,这表明“活动已被破坏”(见的logcat在的结束邮政)。这是否意味着fragA已被销毁,因此我无法添加fragB到它,还是意味着fragB已被销毁,因此我无法将其添加到fragA?或者它完全意味着别的什么?

MainActivity.java

主活动.java

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentManager fragMan = getSupportFragmentManager();

        // add Fragment A to the main linear layout
        FragmentTransaction fragTrans = fragMan.beginTransaction();
        FragmentClassA fragA = new FragmentClassA();
        fragTrans.add(R.id.mainLinearLayout, fragA);
        fragTrans.addToBackStack("A");
        fragTrans.commit();

        // get Fragment A to add a Fragment B to itself
        fragA.addFragB();
    }

}

activity_main.xml

活动_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <LinearLayout
        android:id="@+id/mainLinearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:orientation="horizontal" >
    </LinearLayout>

</RelativeLayout>

FragmentClassA.java

片段类A.java

public class FragmentClassA extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_a, container, false);
    }

    public void addFragB() {
        FragmentManager childFragMan = getChildFragmentManager();

        FragmentTransaction childFragTrans = childFragMan.beginTransaction();
        FragmentClassB fragB = new FragmentClassB();
        childFragTrans.add(R.id.fragA_LinearLayout, fragB);
        childFragTrans.addToBackStack("B");
        childFragTrans.commit();

    }
}

fragment_a.xml

片段_a.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragA_LinearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >


</LinearLayout>

FragmentClassB.java

片段类B.java

public class FragmentClassB extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_b, container, false);
    }

}

fragment_b.xml

片段_b.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>

LogCat

日志猫

11-18 16:17:05.627: E/AndroidRuntime(14351): FATAL EXCEPTION: main
11-18 16:17:05.627: E/AndroidRuntime(14351): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.nestedfragmenttest/com.example.nestedfragmenttest.MainActivity}: java.lang.IllegalStateException: Activity has been destroyed
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.ActivityThread.access0(ActivityThread.java:141)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.os.Handler.dispatchMessage(Handler.java:99)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.os.Looper.loop(Looper.java:137)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.ActivityThread.main(ActivityThread.java:5103)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at java.lang.reflect.Method.invokeNative(Native Method)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at java.lang.reflect.Method.invoke(Method.java:525)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at dalvik.system.NativeStart.main(Native Method)
11-18 16:17:05.627: E/AndroidRuntime(14351): Caused by: java.lang.IllegalStateException: Activity has been destroyed
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1365)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at com.example.nestedfragmenttest.FragmentClassA.addFragB(FragmentClassA.java:26)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at com.example.nestedfragmenttest.MainActivity.onCreate(MainActivity.java:25)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.Activity.performCreate(Activity.java:5133)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
11-18 16:17:05.627: E/AndroidRuntime(14351):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
11-18 16:17:05.627: E/AndroidRuntime(14351):    ... 11 more

EDIT:

编辑:

ianhanniballake's answer and GrIsHu's later answer which expands on the same point have both been helpful in pointing out the root of the problem. However this raises a further issue.
The final intention is that FragmentClassA will be part of a library. It will be used in multiple situations and the number of FragmentClassB instances will vary, or there may even be none. Hence I need to be able to trigger the addition of the child fragments to any instance of FragmentClassA from the parent activity. I've just had a look at keeping fragAas a class level variable in MainActivityand then calling fragA.AddFragB()in the MainActivity's onActivityCreated()method, but it is not available to be overridden. Any thoughts?

ianhanniballake 的回答和 GrIsHu 后来在同一点上展开的回答都有助于指出问题的根源。然而,这又引发了另一个问题。
最终目的是 FragmentClassA 将成为库的一部分。它将在多种情况下使用,并且 FragmentClassB 实例的数量会有所不同,甚至可能没有。因此,我需要能够从父活动触发将子片段添加到 FragmentClassA 的任何实例。我刚刚看了一下保持fragA作为一个类级别的变量MainActivity,然后调用fragA.AddFragB()MainActivityonActivityCreated()方法,但它是不可被覆盖。有什么想法吗?

采纳答案by GrIsHu

You can not directly load the fragment which you have declared in FragA. The FragmentA will be loaded first and then after you can load the FragmentB by calling the method addFragB()from your fragA'sonCreateView()method.

您不能直接加载您在FragA. FragmentA 将首先加载,然后您可以通过addFragB()从您的fragA'sonCreateView()方法调用该方法来加载 FragmentB 。

Try out as below:

尝试如下:

Remove the line fragA.addFragB();from your MainActivity

fragA.addFragB();MainActivity 中删除该行

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FragmentManager fragMan = getSupportFragmentManager();

        // add Fragment A to the main linear layout
        FragmentTransaction fragTrans = fragMan.beginTransaction();
        FragmentClassA fragA = new FragmentClassA();
        fragTrans.add(R.id.mainLinearLayout, fragA);
        fragTrans.addToBackStack("A");
        fragTrans.commit();

    }

}

And try to load the FragmentBfrom FragmentAas below:

并尝试FragmentBFragmentA以下加载:

public class FragmentClassA extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        addFragB(); //Call and load fragment B here.
        return inflater.inflate(R.layout.fragment_a, container, false);
    }

    public void addFragB() {
        FragmentManager childFragMan = getChildFragmentManager();

        FragmentTransaction childFragTrans = childFragMan.beginTransaction();
        FragmentClassB fragB = new FragmentClassB();
        childFragTrans.add(R.id.fragA_LinearLayout, fragB);
        childFragTrans.addToBackStack("B");
        childFragTrans.commit();

    }
}

回答by ianhanniballake

FragmentTransaction.commitis not an immediate action as per the documentationtherefore your fragAis not attached to an Activity (hence why it returns that the activity is destroyed) when you call fragA.addFragB().

FragmentTransaction.commit根据文档,这不是立即操作,因此fragA当您调用fragA.addFragB().

You should instead call addFragB()in FragmentClassA.onCreate()to ensure fragAis attached to an activity and ready to initialize its own state.

你应该改为调用addFragB()FragmentClassA.onCreate()确保fragA连接到一个活动,并准备初始化它自己的状态。

回答by Oliver.Oakley

Did you try adding this to child fragments:

您是否尝试将其添加到子片段中:

@Override
    public void onDetach() {
        super.onDetach();

        try {
            Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
            childFragmentManager.setAccessible(true);
            childFragmentManager.set(this, null);

        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    } 

this will avoid runtime problems while switching between different fragments.

这将避免在不同片段之间切换时出现运行时问题。

Let's say you have an activity with a Fragment, which will in turn have two child fragments: fragAand FragB.

假设您有一个带有 Fragment 的活动,它又将有两个子 Fragment:fragAFragB

You can implement child fragments something like this:

你可以实现这样的子片段:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.activity_mymessages, container, false);
    FragmentManager manager = getChildFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.headlines, fragA);
    ft.replace(R.id.article, fragB);
    ft.commit();

    return root;
}

activity_mymessages.xml

activity_mymessages.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal">
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal">
        <FrameLayout android:id="@+id/headlines"
            android:layout_height="match_parent"
            android:name="com.example.fragA"
            android:layout_width="103dp"
            android:layout_marginRight="10dp"/>
        <FrameLayout android:id="@+id/article"
            android:layout_height="fill_parent"
            android:name="com.example.fragB"
            android:layout_width="fill_parent" />
    </LinearLayout>

</FrameLayout>

Let me know if this works, if not I can provide working code from my GitHub.

让我知道这是否有效,如果无效,我可以从我的 GitHub 提供工作代码。