Fragment生命周期问题:getView()为null

时间:2013-04-30 15:52:04

标签: android android-fragments android-lifecycle

我有一个MainActivity,其布局包括两个FrameLayout来加载Fragments,一个用于带顶部菜单的顶部栏,另一个用于内容。

当我点击设备的主页按钮并再次启动应用程序时,一切正常。

例如,当我启动应用程序时,单击菜单选项2,它将内容片段替换为下面显示的SomeFragment实例,然后单击设备的主页按钮,然后再次启动应用程序,它会重新启动SomeFragment Fragment符合预期,但我的应用在TopBarFragment崩溃,因为getView()为空而返回错误。

我想我正在弄乱Fragment生命周期,但我找不到解决方案。

注意:为了测试我的应用,我在设备的开发者选项中选中了Don't keep activities选项。

有人可以帮忙吗?

修改

重新启动应用时,onResume实例中未调用TopBarFragment。为什么?

调用的实例
((TopBarFragment)getActivity().getSupportFragmentManager()
    .findFragmentByTag("top_bar_fragment")).setSelectedButton(1);

似乎是“空的”......为什么?

编辑2:更多实验

点击设备的主页按钮时,onDetach会调用onDestroyViewTopBarFragment,那么为什么在重新启动应用时不会再次调用onCreateView

编辑3:更多实验

重新启动应用时,会调用MainActivity onCreate,所以

fragmentTransaction.replace(R.id.content_container, homeFragment, "");

已执行。但是,它会加载SomeFragment,这很好,因为它是Fragment中加载的最后一个content_container,但出乎意料,因为MainActivity onCreate被调用了加载HomeFragment ...

栈跟踪

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.package/com.my.package.MainActivity}: java.lang.NullPointerException
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1967)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
    at android.app.ActivityThread.access$600(ActivityThread.java:127)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1158)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4511)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
    at com.my.package.TopBarFragment.setSelectedButton(TopBarFragment.java:345)
    at com.my.package.SomeFragment.onCreateView(SomeFragment.java:50)
    at android.support.v4.app.Fragment.performCreateView(Fragment.java:1460)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:911)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070)
    at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1861)
    at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:547)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1136)
    at android.app.Activity.performStart(Activity.java:4480)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1940)
    ... 11 more

MainActivity.java

public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);     

        setContentView(R.layout.main);      

        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();

        Fragment homeFragment = new HomeFragment();
        fragmentTransaction.replace(R.id.content_container, homeFragment, "");

        Fragment topBarFragment = new TopBarFragment();
        fragmentTransaction.replace(R.id.top_bar_container, topBarFragment, "top_bar_fragment");

        fragmentTransaction.commit();
    }
}

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

    <FrameLayout
        android:id="@+id/content_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="44dp" />

    <FrameLayout
        android:id="@+id/top_bar_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false" />

</RelativeLayout>

TopBarFragment.java

public class TopBarFragment extends Fragment{   

    private int mSelectedMenuOption = 0;

    private LinearLayout mVerticalMenu;

    private Boolean mMenuIsOpen = true;

    private ImageButton btn_01, btn_02; // there are more


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        return inflater.inflate(R.layout.top_bar, container, false);
    }

    @Override
    public void onActivityCreated (Bundle savedInstanceState){

        super.onActivityCreated(savedInstanceState);

        btn_01 = (ImageButton) getView().findViewById(R.id.btn_01);
        btn_02 = (ImageButton) getView().findViewById(R.id.btn_02);

        btn_01.setOnClickListener(mButtonClickListener);
        btn_02.setOnClickListener(mButtonClickListener);

        mVerticalMenu = (LinearLayout) getView().findViewById(R.id.vertical_menu);

        toggleMenu(0);

        Button btn_menu = (Button) getView().findViewById(R.id.btn_menu);
        btn_menu.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // toggle vertical menu             

            }
        });
    }


    private OnClickListener mButtonClickListener = new OnClickListener()
    {

        @Override
        public void onClick(View v) {

            /* ... */

            if(!v.isSelected()){

                FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();

                switch(v.getId()){

                case R.id.btn_01:

                    Fragment homeFragment = new HomeFragment();     
                    fragmentTransaction.replace(R.id.content_container,homeFragment, "");
                    fragmentTransaction.commit();

                    break;

                case R.id.btn_02:

                    Fragment someFragment = new SomeFragment();     
                    fragmentTransaction.replace(R.id.content_container, someFragment, "");
                    fragmentTransaction.commit();

                    break;      
                }
            }
        }
    };

    public void setSelectedButton(int i){

        // Crashes when starting the app, clicking on btn_02 to load a SomeFragment instance, clicking on the 
        // device's Home button, and starting the app again: getView() is null
        // why?

        /* ... */            

        mSelectedMenuOption = i;
    }
}

SomeFragment

public class SomeFragment extends Fragment{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        ((TopBarFragment)getActivity().getSupportFragmentManager().findFragmentByTag("top_bar_fragment")).setSelectedButton(1);

        return inflater.inflate(R.layout.some_fragment, container, false);
    }
}

2 个答案:

答案 0 :(得分:0)

您按照docs按正确顺序执行此操作,onActivityCreated位于onCreateView之后。我知道在用户使用getView()的SO上看到了很多有用的例子,但我总是使用getActivity()代替我将onStart声明放在我的@Override public void onActivityCreated (Bundle savedInstanceState){ super.onActivityCreated(savedInstanceState); btn_01 = (ImageButton) getActivity().findViewById(R.id.btn_01); btn_02 = (ImageButton) getActivity().findViewById(R.id.btn_02); btn_01.setOnClickListener(mButtonClickListener); btn_02.setOnClickListener(mButtonClickListener); mVerticalMenu = (LinearLayout) getActivity().findViewById(R.id.vertical_menu); toggleMenu(0); Button btn_menu = (Button) getActivity().findViewById(R.id.btn_menu); btn_menu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // toggle vertical menu } }); } 中,它总是对我很好。您可以尝试以下代码:

{{1}}

答案 1 :(得分:0)

在发布的堆栈跟踪中,似乎在构建内容视图之前,在setSelectedButton(1)(您最有可能更新/使用其视图)上调用TopBarFragment方法。在事务之前移动TopBarFragment替换事务以替换内容视图,以便在内容片段尝试访问它之前构建其视图。