-1

I want to define a single nav drawer I can use throughout my application. I followed the instructions of the chosen answer here as my first approach: Same Navigation Drawer in different Activities

I made a few modifications, namely calling onCreateDrawer from inside an overridden onCreate. I updated my subsequent activity to extend DashboardActivity ("base activity" from the example). When I launch my second activity I get a null pointer exception complaining that the nav UI doesn't exist when onCreateDrawer tries to set the toggle listened on the drawer.

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.widget.DrawerLayout.setDrawerListener(android.support.v4.widget.DrawerLayout$DrawerListener)' on a null object reference

Here are the base (dashboard) and subsequent (log workout) activity - please ask if there is other code you want to see. The code for the UI of the drawer and associated activity are what came out of the box when creating a new Nav Drawer Activity in Android Studio.

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

        onCreateDrawer();

        Realm realm = Realm.getDefaultInstance();

        RealmQuery<Exercise> query = realm.where(Exercise.class);
        RealmResults<Exercise> result = query.findAll();
        Log.d(TAG, "There are " + result.size() + " exercises ready for use.");

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent logWorkoutIntent = new Intent(getApplicationContext(), LogWorkoutActivity.class);
                startActivity(logWorkoutIntent);
            }
        });

        //TODO: Remove this sign out button
        Button signOutButton = (Button) findViewById(R.id.button_sign_out);
        signOutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FirebaseAuth.getInstance().signOut();
                Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
                startActivity(intent);
                Toast.makeText(getApplicationContext(), "Signed out", Toast.LENGTH_LONG).show();
            }
        });

        //TODO: Remove this data tester
        updateUserName();
    }

    //@Override
    protected void onCreateDrawer() {
        Log.d(TAG, "onCreateDrawer called");
        //super.onCreate(savedInstanceState);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, mDrawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        mDrawerLayout.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

...

-

LogWorkoutActivity.java
public class LogWorkoutActivity extends DashboardActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_log_workout);
        setContentView(R.layout.activity_log_workout);
        super.onCreateDrawer();
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        //getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }

}
Community
  • 1
  • 1
justinraczak
  • 776
  • 2
  • 9
  • 24

1 Answers1

0

I think you are missing a drawer with id drawer_layout in your activity_log_workout layout.

In order for this approach to work, you must have a DrawerLayout with id drawer_layout in all your activity layouts that should have the drawer.

I also noticed something peculiar with your code. In every activity that extends DashboardActivity you are first setting setContentView(R.id.activity_dashboard), then calling onCreateDrawer(), then you are changing the content view and creating the drawer again. I think this a very suboptimal approach.

I suggest you create a BaseDrawerActivity class to encapsulate the drawer creation and UI binding logic. Then you just extend it in the activities where you need a drawer. You can do it like this:

public abstract class BaseDrawerActivity extends AppCompatActivity {

    // move all your drawer related fields here

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

        setContentView(getLayoutResId());

        // the same method you have right now
        onCreateDrawer();
    }


    /*
     * Extending activities use this class to supply the 
     * id of their layout file. This way you can set the view 
     * only once and there is no need to create the drawer twice.
     */
    @LayoutResId
    public abstract int getLayoutResId();

}
Danail Alexiev
  • 7,624
  • 3
  • 20
  • 28
  • Thanks so much for the detailed answer. I guess I hadn't looked closely enough at the control flow. (I'm quite new to Android and this is the first time I've subclassed a class of my own.) Would you be able to provide any insight into implementing the XML for this? When I thought about abstracting the nav drawer to its own class I wasn't sure how the UI would work since the drawer needs to wrap the actual content in the XML. – justinraczak Oct 05 '16 at 16:10
  • @justinraczak The XML for the drawer should always be included in every Activity you want a NavigationDrawer. So if you go back to my answer and copy the XML I've written there. This should be the base for every Activity you want to include a NavigationDrawer. Here is the link again: http://stackoverflow.com/a/19451842/2767703 – Kevin van Mierlo Oct 06 '16 at 07:43
  • @KevinvanMierlo Thank you - I'm unloading some of what I was playing with and trying this now. – justinraczak Oct 06 '16 at 15:16
  • @KevinvanMierlo In setting this up again I remembered the problem I was trying to get around. My subsequent action loads and does have the nav toggle in the upper left, but when I tap it it performs the back action to the previous activity instead of opening the drawer. Do you know why that would be? – justinraczak Oct 06 '16 at 15:27
  • @justinraczak Do you have `if (drawerToggle.onOptionsItemSelected(item) { return true; }` in your `onOptionsItemSelected`? This should be above the check if `android.R.id.home` is pressed – Kevin van Mierlo Oct 07 '16 at 07:35
  • @KevinvanMierlo I didn't. Adding the line did get the drawer responsive in the second activity. It seems I might be missing other bits, though, since I don't have the check for a home button press you mentioned, either. My `onOptionsItemSelected` looks like this now: https://gist.github.com/jraczak/45db74dc5163eee4dab5777cc73ef8f6 – justinraczak Oct 07 '16 at 14:56
  • @justinraczak Sorry for the late response, in the middle of moving to another house. You don't have to have the check for home button press. I don't really remember why I have it. Your onOptionsItemSelected looks good. If it works as expected you should be fine – Kevin van Mierlo Oct 11 '16 at 08:18