Search code examples
androiddrawerlayout

dynamically change the drawer layout


I have this situation: when launching the app, the first fragment (A) displayed has a list of some users. When clicking a user, another fragment (B)is displayed and, together, the drawer menu will be different for each user.

As you can see, I can NOT set up the DrawerLayout when launching the app (the main activity or Launcher) since I don't have the data for the listView yet, but have to set it up when clicking a user in the list in fragment A after the app finishes launching (That's when I can retrieve the data for the listView of the drawer menu, which is indicated by the id: fragment_drawer in below xml file).

Here's the xml file for "MainActivity":

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!-- The navigation drawer, listView is inside MyDrawerFragment's layout-->
    <fragment android:id="@+id/fragment_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:name="mypackage.com.MyDrawerFragment"
        tools:layout="@layout/drawer_layout" />
</android.support.v4.widget.DrawerLayout>

How can I achieve the goal in fragment B? Is this achievable at all in fragment B?

If not possible doing this in fragment B, I am thinking to launch a second activity, say Activity2, and the above xml layout file will be applied to this new activity, when clicking the user in fragment A, I just keeps doing this: closing the existing Acitvity2 by calling finish() and create a new Activity2 instance with new data for drawer menu? Is this approach feasible at all?

Any hints are highly appreciated!

Thanks, Shawn


Solution

  • I had to do an app where I had to make an Activity with 4 fragments and a ViewPager, where only in 2 I would open a different Drawer.

    The Activity's layout must contain the DrawerLayout*, but since the Drawer itself depends on which Fragment I'm on, I figured that it should be the Fragment the one in charge of rendering (or not) the Drawer.

    *If the Activity doesn't contain the DrawerLayout, it wouldn't be displayed filling the whole screen!


    I did something like this, though it does need some refactoring and clean up :)

    Working on the idea that the Fragment is in charge of rendering the Drawer, but it is the Activity the one that has access to it, I made two interfaces to communicate Fragment and Activity:

    /** 
     * should be implemented by any fragment interested in communicating 
     * with a {@link FragmentListener} Activity
     */
    public interface ActivityListener {
        /**
         * called from {@link FragmentListener} requestOpenDrawer
         * the fragment is in charge of rendering its drawer's layout
         */
        public void renderDrawer(DrawerLayout mDrawerLayout, 
                            NavigationView viewById, Activity activity);
    }
    

    -

    /** 
     * used to communicate FROM fragments, to its parent Activity
     * implemented by {@link MainTabbedActivity}
     */
    public interface FragmentListener {
        /**
         * call from a fragment to request opening the drawer
         * note that if the drawer isn't opened by the activity, 
         * it wouldn't cover the whole screen
         */
        void requestOpenDrawer(Fragment requester);
    
        void requestCloseDrawer();
    
        DrawerLayout getDrawerLayout();
    }
    

    Implementation

    @Override
    public void requestOpenDrawer(Fragment requester) {
        mDrawerLayout.openDrawer(Gravity.RIGHT); //Edit Gravity.End need API 14
        if (requester instanceof ActivityListener) {
            NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    
            //let fragment render the drawer
            ((ActivityListener) requester).renderDrawer(mDrawerLayout, navigationView, this);
        }
    }
    

    -

    @Override
    public void renderDrawer(DrawerLayout mDrawerLayout, NavigationView viewById, Activity activity) {
       View child = activity.getLayoutInflater().inflate(R.layout.credit_fragment_drawer, null);
    
        navigationView.addView(child);
    }
    

    Layouts

    Both the Activity layout and the Fragments layout don't have anything more than the standard Drawer implementation:

    Activity layout with Drawer:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main_tabbed_drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:openDrawer="right">
    
        <include
            layout="@layout/activity_main_tabbed_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="right"
            android:fitsSystemWindows="true" />
    
    </android.support.v4.widget.DrawerLayout>
    

    Where activity_main_tabbed_content is whatever the content you want to put in the Activity.

    The fragments really don't have anything more than a common Fragment. Example:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <!-- whatever views you want -->
    </RelativeLayout>