Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple activities (not fragments) with a NavigationDrawer. How to show currently selected Activity?

I had an application with quite a few activities before it was decided that we will be using a Navigation Drawer or a hamburger menu. I did not want to redo the whole app using fragments so I decided to go with the approach used in this answer: Same Navigation Drawer in different Activities

EDIT: And now, this one https://stackoverflow.com/a/23477100/1371585

And I created a base activity called NavDrawerBaseActivity. Here is the code:

    public class NavDrawerBaseActivity extends MyBaseActivity {

    public DrawerLayout mNavDrawerLayout;
    public ListView mDrawerList;
    public String[] mMenuItems;

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

        mNavDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mMenuItems = getResources().getStringArray(R.array.optionsmenu_array);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                R.layout.drawer_list_item, mMenuItems);
        mDrawerList.setAdapter(adapter);
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener(this));

        super.onCreate(savedInstanceState);
    }

    private class DrawerItemClickListener implements
            ListView.OnItemClickListener {

        private DrawerItemClickListener(Context context) {
            mContext = context;
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {

            String textClicked = ((TextView) view).getText().toString();
            view.setSelected(true);

            if (textClicked.equalsIgnoreCase(mContext
                    .getString(R.string.optionsmenu_library))) {


                Intent libraryIntent = new Intent(mContext,
                        LibraryActivity.class);
                libraryIntent.putExtra("navdrawerposition", position);
                startActivity(libraryIntent);
                mNavDrawerLayout.closeDrawers();
                mDrawerList.setItemChecked(position, true); //Not working


            } else if (textClicked.equalsIgnoreCase(mContext
                    .getString(R.string.optionsmenu_settings))) {
                // TODO: open activity and close the drawer
            } else if (textClicked.equalsIgnoreCase(mContext
                    .getString(R.string.optionsmenu_logout))) {
                // TODO: open activity and close the drawer
            } 
        }

        private Context mContext;
    }
}

Here is the layout file navdrawer_activity.xml

    <?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    >

    <FrameLayout
        android:id="@+id/activity_frame"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <!-- The navigation drawer -->

    <ListView
        android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#111"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp" 
        />


</android.support.v4.widget.DrawerLayout>

Every activity extends the NavDrawerBaseActivity and does not use setContentView, like so:

 public class LibraryActivity extends NavDrawerBaseActivity {

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

        // Not setting content view here, since its already set in
        // NavDrawerBaseActivity
        FrameLayout frameLayout = (FrameLayout) findViewById(R.id.activity_frame);
        // Inflating the Camera activity layout
        LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View activityView = layoutInflater
                .inflate(R.layout.library_page, null, false);
        // Adding the custom layout of this activity to frame layout set in
        // NavDrawerBaseActivity.
        frameLayout.addView(activityView);


//      Only this part of the code is doing what I want.

//      int drawerSelectedPosition = getIntent().getIntExtra(mNavDrawerPosExtraName, -1);
//      if(drawerSelectedPosition > -1){
//          mDrawerList.setItemChecked(drawerSelectedPosition, true);
//      }
    }

}

My problem: How do I correctly highlight the current activity in the NavDrawer View? The mDrawerList.setItemChecked(position, true); before launching Intent or after launching it is not working.

The weird part is: If I am currently in Activity1, Open the NavDrawer and select Activity2. I land in Activity2, open the NavDrawer and see that "Activity2" is not selected. I click the Back button, land in Activity1, open the NavDrawer and see that "Activity2" is selected.

Which means setItemChecked works, but not in the new activity that gets launched.

Currently I am passing the position as an Intent extra and specifically setting the checked position, like the commented section in LibraryActivity. This works but seems like a work around. Please tell me if there is a correct/better way of doing that in NavDrawerBaseActivity class instead of in each Activity that extends it.

like image 929
ForeverNoob Avatar asked Nov 09 '22 22:11

ForeverNoob


1 Answers

Do not put a nav drawer in every activity, this defeats the purpose of extending the NavDrawerBaseActivity class. Because all your other activities extend this base class they should automatically inherit all its functionality. Hence, only put the drawer in the NavDrawerBaseActivity. Then, in your xml for the drawer you can specify each buttons action like:

<Button
        style="@style/drawerBtn"
        android:id="@+id/activity1Btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick">

Or simply set an onClick listener on your drawer, something like:

myDrawer.setOnClickListener().........etc

Then in your onClick handler (which is inside the NavDrawerBaseActivity class) you can simply check which button was pressed and open the associated activity, something like:

//remember, if you used the onClickListener you have to use @Override at the top of this function
public void onClick(View item) {

    //lets see which button on the drawer was pressed....item is the item that triggered the click
    switch (item.getId()) {
        case R.id.activity1Btn:
            Intent intent1 = new Intent(this, Activity1.class);
            startActivity(intent1);
            break;
        case R.id.activity2Btn:
            Intent intent2 = new Intent(this, Activity2.class);
            startActivity(intent2);
            break;
        case R.id.activity3Btn:
            Intent intent3 = new Intent(this, Activity3.class);
            startActivity(intent3);
            break;
    }
}

Remember that Activity1, Activity2, Activity3 will have to extend NavDrawerBaseActivity which then either have to extend Activity or extend another class which in turn extends Activity...... and so on and so forth. You can then also set things like mDrawerList.setItemChecked(position, true) in this switch. So in short, make all drawer things happen in this class only and simply "implement" this class by extending it, remember this class contains all functionality that is common across all the "child" classes/activities, they all inherit this behaviour

EDIT:

If you want to highlight the items in your drawer and you must rather use setSelected(true) on your item. You can define custom selection states if you want to by creating a selection style in your drawables folder. You then set this style as the background of your list items example:

<!-- drawable/myStyles.xml-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true" android:state_pressed="true" android:drawable="@color/blue"/> <!--item selected state--> 
    <item android:state_enabled="true" android:state_focused="true" android:drawable="@color/blue"/> <!--item selected state--> 
    <item android:state_enabled="true" android:state_selected="true" android:drawable="@color/blue"/> <!--item selected state--> 
    <item android:state_focused="false" android:state_pressed="false" android:drawable="@color/gray"/> <!--item NOT selected state--> 
</selector>

and your drawer item:

<LinearLayout
    android:id="@+id/my_drawer_item"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/myStyles">

Then in your switch/if-else statement you set the selected item as myNewSelectedView.setSelected(true). You might have to manually deselect the old one like myOldSelectedItem.setSelected(false)

Then click/selected listener where you have you switch/if-else statement:

 @Override
public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
     view.setSelected(true)  

     //....the rest of your code here
}

Lastly I recommend you try this first as this will be the easiest route to follow if you use a normal listView:

<ListView android:id="@+id/my_list"
    android:choiceMode="singleChoice" 
    android:listSelector="@android:color/blue" />

EDIT2:

Now to retain the selected items state...So it seems that each action on the drawer reinstantiates the base class which isnt ideal. We need to somehow retain the instance state so it acts almost as a singleton. I've tried overriding the the onSaveInstanceState and using the singleInstance launcher state but they did not work. So, for the interim I've come up with the solution of saving the current selection in memory as a static variable:

private static int mCurrentSelectionIndex = 0; //this is defined at the top of your class with your default selected screen ie the first item in the list.

//then in setContentView after assigning the drawer layout you set the currentSelection
@Override
public void setContentView(final int layoutResID) {
    //...... first assign the layouts ie mDrawerList = findViewByLayout(R.id.myDrawerList) etc

    mDrawerList.setSelection(mCurrentSelectionIndex); 
      // OR:
    mDrawerList.setItemChecked(mCurrentSelectionIndex, true);
}

//then in onClick()
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    mCurrentSelection = position;
    item.setSelected(true);
}
like image 98
Chris Avatar answered Nov 14 '22 23:11

Chris