简体   繁体   中英

Navigation Drawer onNavigationDrawerItemSelected called before MainActivity onCreate?

I have setup a new project with the template implementation of Navigation Drawer Fragment and a MainActivity.

It provides me with the following relevant methods:

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

    Intent intent = getIntent();
    token = intent.getStringExtra(EXTRA_TOKEN);

    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
    mNavigationDrawerFragment.activityMain = this;

    mTitle = getTitle();

    // Set up the drawer.
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout));
}

My MainActivity is started by a splash activity which gets a saved access token via the EXTRA_TOKEN.

This is the override of the Navigation Drawer item select listener in the MainAcitivity:

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getSupportFragmentManager();
    onSectionAttached(position + 1);

    switch(position) {
        case 0:
            fragmentManager.beginTransaction()
                    .replace(R.id.container, FeedFragment.newInstance(token, ""))
                    .commit();
            break;

        case 1:
            fragmentManager.beginTransaction()
                    .replace(R.id.container, PeopleFragment.newInstance("", ""))
                    .commit();
            break;

        case 2:
            if(qbloggedin) {
                fragmentManager.beginTransaction()
                        .replace(R.id.container, MessagesFragment.newInstance(token, ""))
                        .commit();
            }
            break;

        default:
            break;
    }
}

It starts three different fragments depending on which item is selected in the NavDrawer. While instantiating the new fragments, the token string is passed into its constructor, which is saved in the fragment's class for further use.

On the first start of the App however, it seems that onNavigationDrawerItemSelected is called before onCreate ! This results me passing a null value token into the fragments, causing them to be all messed up.

How is this possible? As I understand it, the NavigationDrawerFragment should not have been setup yet!

I set breakpoints on both onCreate and on onNavigationDrawerItemSelected switch position = 0 . onNavigationDrawerItemSelected is indeed hit before onCreate .

How can I make sure to get the token first before trying to handle the onNavigationDrawerItemSelected ?

Any help would be appreciated.

I believe I figured this out as it was happening to me for anyone who searches this and can't find the answer.

If you use the Android Studio DrawerActivity then there is boilerplate code that they create for you. In this code in the activity_main.xml or whichever XML your DrawerActivity sets as its' content view, there is a tag.

When setContentView() is called in onCreate(), this fragment is automatically created and so technically onCreate() is still being called first but then the onNavigationDrawerItemSelected() method is called before anything else in create. Since setContentView is typically kept up top, this causes problems when trying to store the state of the fragments in your drawer.

Simply move any code that checks for savedInstanceBundle above setContentView() and it will fix the problem.

Example with comments:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // THIS IS WHERE YOU CHECK FOR SAVED INSTANCE
    // Check for frag
    if (savedInstanceState != null) {
        Log.i(TAG, "Get QuestionDayFragment");
        mQuestionDaysFragment = (QuestionDaysFragment) getSupportFragmentManager().getFragment(savedInstanceState, QUESTION_DAY_FRAGMENT);
    }

    // View injection
    setContentView(R.layout.activity_main);
    ButterKnife.inject(this);

    // THIS IS WHERE THE CODE WAS BEFORE
    // THIS WOULD BE CALLED AFTER onNavigationDrawerItemSelected()

    // Singleton injection
    LifeboxApplication.graph().inject(this);

    // Toolbar
    setSupportActionBar(mToolbar);

    // FB
    uiHelper = new UiLifecycleHelper(this, callback);
    uiHelper.onCreate(savedInstanceState);

    // Drawer
    mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
    mTitle = getTitle();
    mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));

}

You could move the intent to a constructor and save your tokens there like so:

Intent i;

......

public FragmentConstructor() {

     i = getIntent();
     token = intent.getStringExtra(EXTRA_TOKEN);

}

What I had to do to make it work was to check if the page has loaded before executing onNavigationDrawerItemSelected

    private Boolean loaded=false;

    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);           

            // Your code here
            this.loaded=true;
    }

    public void onNavigationDrawerItemSelected(int position) {
        if (!this.loaded){
            return;
    }

I also agree with using a boolean to check if onCreate() has finished loading. My only other suggestions is that for a quick fix you can use onSectionAttached(int number) to process each item selected instead of onNavigationDrawerItemSelected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM