在父Fragment视图已创建后添加嵌套Fragment

15

我正在尝试创建一个Fragment,其中包含一个公共方法,用于向其自身添加子Fragment。

我已经阅读了一些可能类似的问题,但目前还没有找到任何有助于解决问题的内容。我将问题简化为了下面展示的一个简单的测试应用程序。

一旦将fragA添加到主布局中,我调用公共方法fragA.addFragB()来使其向自身添加一个FragmentClassB的实例,但这会导致测试应用程序崩溃,提示“Activity has been destroyed”(请参见本文末尾的LogCat)。这是不是意味着fragA已被销毁,因此我不能将fragB添加到它上面,还是意味着fragB已被销毁,因此我不能将其添加到fragA上?还是说它意味着完全不同的事情?

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

<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

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

<?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

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

<?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.access$600(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

编辑:

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

3个回答

13

你不能直接加载在FragA中声明的片段。需要先加载FragmentA, 然后在fragA's onCreateView()方法中调用addFragB()方法来加载FragmentB。

尝试如下:

从你的MainActivity中删除fragA.addFragB();这一行。

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();

    }

}

尝试按照以下方式从FragmentA加载FragmentB:

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();

    }
}

这很有帮助。谢谢。请检查一下我的问题编辑。 - Sound Conception
你想要实现的东西有点复杂,而且不是有效的实现方式。你能告诉我FragmentA类将在哪个库中? - GrIsHu
澄清一下,FragmentA 将成为 Android Library 项目的一部分。它的功能类似于图形编辑器、文字处理器等中的工具栏。工具栏的内容取决于应用程序中打开的文件类型的逻辑。我认为使用嵌套片段可能会很方便,以根据需要重新排列这些工具栏。但从听起来的情况来看,我应该坚持使用从自定义复合小部件构建工具栏的旧方法,而不是从子片段构建。这是你的意思吗? - Sound Conception
其实,现在我想更多地考虑一下,我的测试应用程序并没有正确反映出真实情况。当应用程序启动时,工具栏(FragmentA)将被创建但不会被填充。它们只有在用户打开文件后才会被填充,因此 FragmentA 到那时肯定已经存在了。也许我仍然可以使用嵌套片段的方法? - Sound Conception
这个解决方案假定FragmentB是FragmentA的子级。如果FragmentB是FragmentA的同级,而您想在FragmentA加载后加载FragmentB,该怎么办?我怀疑您不能使用getChildFragmentManager来实现。 - Johann
使用 onAttach (Context context) 是否更好,因为这样可以保证父片段已经附加到其活动中。 - F.O.O

4
FragmentTransaction.commit不是立即执行的操作,根据文档,因此当您调用fragA.addFragB()时,您的fragA未附加到Activity上(因此返回该Activity已被销毁)。

相反,您应该在FragmentClassA.onCreate()中调用addFragB(),以确保fragA已附加到Activity并准备好初始化自己的状态。


这很有帮助。谢谢。请检查一下我的问题编辑。 - Sound Conception

0

你尝试将这个添加到子片段了吗:

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

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

假设您有一个带有片段的活动,该片段将进一步拥有两个子片段:fragAFragB

您可以像这样实现子片段:

@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

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

如果这个不行,请告诉我,我可以提供我的GitHub上的可用代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接