Material Design 控件知識梳理(5) DrawerLayout && NavigationView

1、概述

今天,咱們來介紹兩個和側滑菜單有關的MD控件:android

  • DrawerLayout:實現側滑菜單的基礎。
  • NavigationView:做爲側滑菜單佈局的一種實現方式。

2、DrawerLayout

2.1 基本原理

當咱們須要使用到側滑菜單時,能夠經過DrawerLayout來實現,DrawerLayout、側滑菜單佈局、普通佈局這三者的關係爲: bash

layout_gravity決定了將哪一個菜單做爲側滑佈局, DrawerLayout會根據是否聲明瞭 layout_gravity屬性,把它內部的直接子 View分紅兩類:

  • 對於沒有聲明layout_gravity的佈局,那麼它會將它們看成普通佈局,並按照FrameLayout的方式來排列它們。
  • 對於聲明瞭layout_gravity="start"或者layout_gravity="left"的佈局,在普通狀況下會將它們隱藏起來,當從屏幕的最左側往右移動手指時,這個佈局會漸漸展示出來,對於DrawerLayout的全部子View來講,只容許有一個子View的該屬性爲start/left,這就是咱們的側滑佈局。
  • layout_gravity="end/right"和上面相似,只不過它的調出是從屏幕的右側向左側移動。

2.2 簡單事例

下面是一個使用DrawerLayout的最簡單的例子:app

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".DrawerLayoutActivity">
    <!-- 普通佈局 -->
    <FrameLayout
        android:id="@+id/fl_content"
        android:background="@android:color/holo_orange_dark"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:text="我是內容佈局"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </FrameLayout>
    <!-- 側滑佈局 -->
    <include layout="@layout/layout_drawer_normal"/>
</android.support.v4.widget.DrawerLayout>
複製代碼

layout_drawer_normal就是側滑菜單,咱們將它的layout_gravity定義爲start,按照前面的分析,它應當位於屏幕的左側:ide

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_gravity="start"
    android:layout_width="200dp"
    android:background="@android:color/holo_green_dark"
    android:layout_height="match_parent">
    <TextView
        android:text="我是側滑佈局"
        android:layout_gravity="center"
        android:textColor="@android:color/white"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
複製代碼

下面是最終的效果: 佈局

2.3 監聽DrawerLayout的狀態變化

若是咱們但願監聽DrawerLayout狀態的變化,那麼能夠經過下面這個方法:ui

private Toolbar mToolbar;
    private ActionBarDrawerToggle mActionBarDrawerToggle;
    private DrawerLayout mDrawerLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drawer_layout_simple);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {

            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                Log.d("mDrawerLayout", "onDrawerSlide, slideOffset=" + slideOffset);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                Log.d("mDrawerLayout", "onDrawerOpened");
            }

            @Override
            public void onDrawerClosed(View drawerView) {
                Log.d("mDrawerLayout", "onDrawerClosed");
            }

            @Override
            public void onDrawerStateChanged(int newState) {
                Log.d("mDrawerLayout", "onDrawerStateChanged, state=" + newState);

            }
        });

    }
複製代碼

其中openedclosed方法都很好理解,就是對應展開和收起,而onDrawerSlide方法中的slideOffset,則對應於DrawerLayout展開的偏移值,所有展開時爲1,所有收起時爲0onDrawerStateChanged對應於所處的狀態:STATE_IDLE/STATE_DRAGGING/STATE_SETTLINGthis

3、DrawerLayoutToolbar結合使用

3.1 ToolbarNavigationIcon跟隨DrawerLayout變化

下面,咱們看一下把DrawerLayoutToolbar相結合,作出下面的效果,讓ToolbarnavigationIcon跟隨着DrawerLayout變化: spa

當拖動側邊欄的時候, Toolbar的按鈕會跟隨着進行狀態的變化,咱們也能夠經過點擊 Toolbar的按鈕來展開和收起側邊欄,首先看咱們的佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- Toolbar -->
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:background="@android:color/holo_green_dark"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"/>
    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!-- 普通佈局 -->
        <FrameLayout
            android:id="@+id/fl_content"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:text="我是內容佈局"
                android:layout_gravity="center"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </FrameLayout>
        <!-- 側滑佈局 -->
        <include layout="@layout/layout_drawer_normal"/>
    </android.support.v4.widget.DrawerLayout>
</LinearLayout>
複製代碼

Activity中,咱們須要將ToolbarDrawerLayout關聯起來:.net

public class DrawerLayoutActivity extends AppCompatActivity {

    private Toolbar mToolbar;
    private ActionBarDrawerToggle mActionBarDrawerToggle;
    private DrawerLayout mDrawerLayout;

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

    private void initView() {
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true); //1.決定顯示.
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mActionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.drawer_open, R.string.drawer_close); //2.傳入Toolbar能夠點擊.
        mDrawerLayout.addDrawerListener(mActionBarDrawerToggle); //3.監聽變化.
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        //4.同步狀態
        mActionBarDrawerToggle.syncState();
    }
}
複製代碼

這裏須要作的有四步工做:3d

  • Toolbar做爲ActionBar,並調用ToolbarsetDisplayHomeAsUpEnabled,這樣最左邊的Icon才能夠顯示。
  • 實例化ActionBarDrawerToggle,並傳入ToolbarDrawerLayout
  • 經過DrawerLayoutaddDrawerListener方法,讓DrawerLayout的狀態可以回調到ActionBarDrawerToggle
  • onPostCreate中,調用ActionBarDrawerTogglesyncState方法。

3.2 DrawerLayout覆蓋Toolbar

在上面的實現方式中,側滑菜單位於Toolbar的下方,若是咱們但願它覆蓋Toolbar,那麼能夠像下面這樣佈局:

<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">
    <!-- 普通佈局 -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"/>
        <FrameLayout
            android:id="@+id/fl_content"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:text="我是內容佈局"
                android:layout_gravity="center"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </FrameLayout>
    </LinearLayout>
    <!-- 側滑佈局 -->
    <include layout="@layout/layout_drawer_normal"/>
</android.support.v4.widget.DrawerLayout>
複製代碼

最終的效果爲:

3.3 DrawerLayoutToolbar延伸到狀態欄上方

若是咱們但願側滑菜單的區域可以延伸到狀態欄,那麼能夠進行如下三步的修改:

  • 第一步:修改Activitystyle,讓狀態欄透明,並修改狀態欄的顏色:
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!-- 修改狀態欄顏色 -->
        <item name="colorPrimaryDark">@android:color/holo_green_dark</item>
        <!-- 狀態欄透明 -->
        <item name="android:windowTranslucentStatus">true</item>
    </style>
</resources>
複製代碼
  • 第二步:給Activity根佈局設置android:fitsSystemWindows="true"屬性
<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"
    android:fitsSystemWindows="true">
    <!-- 普通佈局 -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"/>
        <FrameLayout
            android:id="@+id/fl_content"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:text="我是內容佈局"
                android:layout_gravity="center"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </FrameLayout>
    </LinearLayout>
    <!-- 側滑佈局 -->
    <include layout="@layout/layout_drawer_normal"/>
</android.support.v4.widget.DrawerLayout>
複製代碼
  • 第三步:給側滑佈局設置android:fitsSystemWindows="true"屬性
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_gravity="start"
    android:layout_width="200dp"
    android:background="@android:color/holo_green_dark"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <TextView
        android:text="我是側滑佈局"
        android:layout_gravity="center"
        android:textColor="@android:color/white"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
複製代碼

經過以上三步,就能夠達到沉浸式狀態欄的效果:

4、NavigationView

4.1 Navigation屬性

在使用DrawerLayout的時候,咱們能夠隨意定義側滑菜單的佈局,NavigationView其實就是Google推薦的側滑佈局應該有的樣子,它的效果相似於下面這樣:

咱們先來看一個使用 Navigation的簡單例子:

<android.support.design.widget.NavigationView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="300dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:headerLayout="@layout/layout_drawer_navigation_header"
    app:menu="@menu/menu_navigation"
    app:itemIconTint="@android:color/white"
    app:itemBackground="@android:color/holo_green_dark"
    app:itemTextColor="@android:color/black">
</android.support.design.widget.NavigationView>
複製代碼

上面app各屬性對應到下圖中就是這樣:

能夠看到, Navigation分爲兩個部分:頭部和列表。

  • 頭部是app:headerLayout所指定的佈局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:src="@drawable/ic_bg"
        android:scaleType="centerCrop"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>
</LinearLayout>
複製代碼
  • 列表經過app:menu所指定:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/favorite"
        android:icon="@mipmap/ic_launcher"
        android:title="收藏"/>
    <item
        android:id="@+id/wallet"
        android:icon="@mipmap/ic_launcher"
        android:title="錢包"/>
    <item
        android:id="@+id/photo"
        android:icon="@mipmap/ic_launcher"
        android:title="相冊"/>
    <item
        android:id="@+id/file"
        android:icon="@mipmap/ic_launcher"
        android:title="文件"/>
</menu>
複製代碼

menu當中的每一個item就對應於列表當中的一項,而itemicontitle則分別對應列表項的圖標和文字,若是但願對item進行分組,那麼能夠採用group的方式來組織menu

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:id="@+id/group1">
        <item
            android:id="@+id/favorite"
            android:icon="@mipmap/ic_launcher"
            android:title="group1 - item1"/>
        <item
            android:id="@+id/wallet"
            android:icon="@mipmap/ic_launcher"
            android:title="group1 - item2"/>
    </group>
    <group android:id="@+id/group2">
        <item
            android:id="@+id/photo"
            android:icon="@mipmap/ic_launcher"
            android:title="group2 - item1"/>
        <item
            android:id="@+id/file"
            android:icon="@mipmap/ic_launcher"
            android:title="group2 - item2"/>
    </group>
</menu>
複製代碼

不一樣組之間就會被分割線隔開:

若是想要給某個分組添加標題,那麼能夠採用 subMenu的方式:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:id="@+id/group1">
        <item
            android:id="@+id/item1"
            android:icon="@mipmap/ic_launcher"
            android:title="group1 - item1"/>
        <item
            android:id="@+id/item2"
            android:icon="@mipmap/ic_launcher"
            android:title="group1 - item2"/>
    </group>
    <group android:id="@+id/group2">
        <item
            android:id="@+id/item3"
            android:icon="@mipmap/ic_launcher"
            android:title="group2 - item1"/>
        <item
            android:id="@+id/item4"
            android:icon="@mipmap/ic_launcher"
            android:title="group2 - item2"/>
    </group>
    <item
        android:id="@+id/item5"
        android:title="group3">
        <menu>
            <item
                android:id="@+id/item6"
                android:icon="@mipmap/ic_launcher"
                android:title="group3 - item1"/>
            <item
                android:id="@+id/item7"
                android:icon="@mipmap/ic_launcher"
                android:title="group3 - item2"/>
        </menu>
    </item>
</menu>
複製代碼

4.2 Navigation點擊監聽

4.2.1 列表點擊監聽

若是但願處理Navigation中列表的監聽,那麼可使用現成的接口,根據itemid來判斷是點擊的是列表中的哪一項。

mNavigationView = (NavigationView) findViewById(R.id.navigation);
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        Log.d("onSelected", "id=" + item.getItemId());
        return true;
    } 
});
複製代碼

4.2.2 頭部點擊監聽

NavigationView並無提供頭部點擊的監聽回調,所以,咱們只可以經過findViewById的方法找到對應的View,對其設置監聽,下面是整個headerLayoutNavigationView中的層次:

5、參考文獻

Android 5.0 之 NavigationView 的使用


更多文章,歡迎訪問個人 Android 知識梳理系列:

相關文章
相關標籤/搜索