Search code examples
androidandroid-layoutmaterial-designnavigation-drawer

Using NavigationView with SlidingPaneLayout


Per Google's Material Design guideline for Navigation drawer, to achieve Standard drawer for tablet or desktop devices, I would use NavigationView with SlidingPaneLayout for tablet devices instead of DrawerLayout for phone devices, which is to achieve Modal drawer.

Standard drawer

I put a NavigationView as the first child view of SlidingPaneLayout. A problem occurred.

As you know SlidingPaneLayout's child views overlap if their combined width exceeds the available width in the SlidingPaneLayout. In this case, the child views expand to fill the available width in the SlidingPaneLayout. The user can slide the topmost view out of the way by dragging it back from the edge of the screen.

But my NavigationView's pane wouldn't slide. It just appears or disappears at maximum width without sliding animation.

How can I solve this?

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- The first child view becomes the left pane. When the combined
         desired width (expressed using android:layout_width) would
         not fit on-screen at once, the right pane is permitted to
         overlap the left. -->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/navigation_view_header"
        app:menu="@menu/navigation_view" />

    <!-- The second child becomes the right (content) pane. In this
         example, android:layout_weight is used to expand this detail pane
         to consume leftover available space when the
         the entire window is wide enough to fit both the left and right pane.-->
    <fragment
        android:id="@+id/navigationHost"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="320dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        app:navGraph="@navigation/main" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>

Solution

  • Add app:elevation="0dp" to the NavigationView.

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:elevation="0dp"
        app:headerLayout="@layout/navigation_view_header"
        app:menu="@menu/navigation_view" />
    

    If you inspect the layout, you can notice the NavigationView has elevation of 16dp by default. This causes the problem. It is probably that SlidingPaneLayout handles the two child views under the condition that they have the same elevations (0dp).

    So a solution is to override NavigationView's default elevation (16dp) with 0dp.


    Another solution is to wrap NavigationView with a FrameLayout or some ViewGroup.

    <FrameLayout
        android:layout_width="280dp"
        android:layout_height="match_parent">
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/navigationView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/navigation_view_header"
            app:menu="@menu/navigation_view" />
    </FrameLayout>
    

    Then, you should specify layout_width with FrameLayout and NavigationView's layout_width to match_parent.

    In spite of NavigationView's default elevation of 16dp, for its parent ViewGroup's elevation is 0dp, SlidingPaneLayout can properly handle.