天天看點

Android 側滑抽屜菜單

側滑抽屜菜單

  • ​​前言​​
  • ​​正文​​
  • ​​一、建立項目​​
  • ​​二、添加滑動菜單​​
  • ​​三、UI美化​​
  • ​​四、添加導航視圖​​
  • ​​五、菜單分類​​
  • ​​六、動态菜單​​
  • ​​七、源碼​​

運作效果圖:

Android 側滑抽屜菜單

前言

  滑動菜單相信都不會陌生,你可能見過很多這樣的文章,但我的文章會給你不一樣的閱讀和操作體驗。

正文

  寫部落格,自然是從建立項目開始了,這樣你可以更好的知道這個過程中經曆了什麼。

一、建立項目

  項目就命名為DrawerDemo,

Android 側滑抽屜菜單

絕對的手把手教學,讓你清楚每一步怎麼做。

然後打開app下的build.gradle,在android{}閉包中添加如下代碼:

//配置JDK的版本
    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
        sourceCompatibility JavaVersion.VERSION_1_8
    }      

這裡是配置JDK的版本,為1.8,我個人最喜歡的版本。一旦項目中的build.gradle有改動,便會出現Sync Now或者Sync的同步提示,如下圖所示:

Android 側滑抽屜菜單

右上角的就是這個同步提示,如果你不點選進行同步,則你剛才在build.gradle中的改動無效,下面點選Sync Now。

二、添加滑動菜單

打開layout,找到activity_main.xml,修改代碼後如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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">

    <!--首頁面布局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center">

        <Button
            android:id="@+id/btn_open"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="打開滑動菜單"
            android:textColor="#000"
            android:textSize="18sp" />
    </LinearLayout>

    <!--滑動菜單布局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/colorAccent"
        android:gravity="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="滑動菜單"
            android:textColor="#000"
            android:textSize="18sp"      

這裡你可以選擇複制粘貼到你的項目中,然後來說說這裡的細節。

第一個細節就是頁面根布局,DrawerLayout ,這個布局現在是在androidx下的,如果你還是v4或者v7的版本下的,請盡早遷移到androidx,至于怎麼遷移?你更新Android Studio就可以了,目前最新的AS是4.1.3版本,你可以選擇4.0.1就可以了,穩妥。

然後你注意到這個裡面放了兩個LinearLayout(線性布局),LinearLayout裡面一個放了TextView,一個放了Button,居中顯示,我這裡特地在布局中增加了注釋,告訴你哪一個是首頁面,哪一個是滑動菜單。

那麼是怎麼分出來的呢?當你第一次看的時候你不知道為什麼的時候,你就找不同,看看兩個LinearLayout有什麼不同,于是你會發現滑動菜單比首頁面布局多了兩個屬性

android:layout_gravity="start"
        android:background="@color/colorAccent"      

然後結果很明顯了,background隻能修改布局的布景,那麼答案就是

android:layout_gravity="start"      

這個屬性是什麼意思呢?layout_gravity值的是布局重力,這裡你要和gravity區分出來,你記住有layout的代表控制本身,沒有layout的代表控制内容。而它的屬性值你看到是start,這種屬性一般都是成對關系,有start自然就有end。而start是Android中新的用法,它代替了left,end代替了right。現在的left和right依然可以用,隻不過Google更推薦你使用start和end表示左右。

那麼你現在再來看這一行代碼就知道是什麼意思了。就是目前布局置于根布局的左側,而因為你的根布局是DrawerLayout,是以你的預覽界面應該隻能看到頁面主布局。

Android 側滑抽屜菜單

但是你要注意左邊的這個藍色線,這個代表了你目前的滑動菜單的位置。

此時你把start改成end,你再看預覽界面就到了右邊去了,表示從螢幕右側出現。記得改回start。

布局介紹完畢了,下面我們通過點選首頁面的按鈕顯示這個滑動菜單。

打開MainActivity,修改後代碼如下:

public class MainActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout;//滑動菜單
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        drawerLayout = findViewById(R.id.drawer_layout);
        
        findViewById(R.id.btn_open).setOnClickListener(v -> {
            //打開滑動菜單  左側出現
            drawerLayout.openDrawer(GravityCompat.START);
        });
    }
}      

這裡我建立一個變量,然後在onCreate中綁定布局id。然後在按鈕的點選事件中,通過openDrawer方法打開滑動菜單,裡面傳入GravityCompat.START,START是左側,GravityCompat是重力相容,表示相容低版本,在低版本的AS中你就要使用Gravity.START。

這裡的START和布局中的start是要對應上的,如果你不對應上就會報錯,那麼下面運作一下吧。

Android 側滑抽屜菜單

嗯,這個效果是有了,但是感覺比較的醜,那麼來美化一下吧。

三、UI美化

打開res下values檔案夾中的styles.xml。

把DarkActionBar改成NoActionBar,意思是去掉預設的導航欄。

Android 側滑抽屜菜單

然後你再運作一下,你會發現好看了一點點。

Android 側滑抽屜菜單

不過螢幕頂部還是有那個很醜的狀态欄,是以我們還需要美化一下。

在MainActivity中增加一個方法來設定狀态欄透明。

/**
     * 透明狀态欄
     */
    private void transparentStatusBar() {
        //改變狀态欄顔色為透明
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }      

這個方法先拿到目前Actvity的DecorView,它是Activity的根部視圖,相當于最底層的視圖,你想要詳細的了解就需要去看源碼了。

然後調用setSystemUiVisibility來改變系統UI的顯示。View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和View.SYSTEM_UI_FLAG_LAYOUT_STABLE表示Activity的布局會顯示在狀态欄上面,之後調用setStatusBarColor方法設定狀态欄顔色為透明。

然後在onCreate中調用這個方法。

Android 側滑抽屜菜單

然後你還需要在activity_main.xml中去設定可以顯示需要顯示在狀态欄中的布局。在首頁面布局和滑動菜單的父布局中都添加一個屬性:

android:fitsSystemWindows="true"      
Android 側滑抽屜菜單

然後再給首頁面設定一個背景圖,背景圖如下:

Android 側滑抽屜菜單

放到你項目的drawable檔案夾下,然後在布局中設定

Android 側滑抽屜菜單

下面運作一下:

Android 側滑抽屜菜單

現在這個感覺怎麼樣呢?比之前是不是好多了呢?但是你會發現這個按鈕有一些不上檔次了,顯得是辣麼的突兀。我們像個辦法去掉它。

首先修改布局,将之前的按鈕替換為如下代碼:

android:id="@+id/toolbar"
            app:navigationIcon="@drawable/icon_menu"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"      

添加位置如下圖

Android 側滑抽屜菜單

這裡的icon_menu是一個圖示,在你的drawable下建立一個icon_menu.xml檔案,裡面的代碼如下:

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#FFFFFF"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M4,18h16c0.55,0 1,-0.45 1,-1l0,0c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1l0,0C3,17.55 3.45,18 4,18zM4,13h16c0.55,0 1,-0.45 1,-1l0,0c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1l0,0C3,12.55 3.45,13 4,13zM3,7L3,7c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1l0,0c0,-0.55 -0.45,-1 -1,-1H4C3.45,6 3,6.45 3,7z"      

這是一個白色的菜單圖示,下面回到MainActivity中,先聲明變量。

private DrawerLayout drawerLayout;//滑動菜單      

然後在onCreate中添加如下代碼:

= findViewById(R.id.toolbar);
        //工具欄按鈕點選
        toolbar.setNavigationOnClickListener(v -> drawerLayout.openDrawer(GravityCompat.START));      

點選之後打開這個滑動菜單。

下面你再運作一下:

Android 側滑抽屜菜單

這樣就簡潔雅緻了很多了。

四、添加導航視圖

  現在我們的滑動菜單用的是一個LinearLayout,雖然用起來沒有很大的問題,但是如果有更好的控件為什麼不用呢?下面就來介紹一下NavigationView,不過要在AS中使用這個控件還需要添加一個依賴庫:

打開你app下的build.gradle,在dependencies{}閉包中添加如下依賴

//添加material庫
    implementation 'com.google.android.material:material:1.2.1'      

添加之後記得點選Sync Now進行同步項目。

然後修改activity_main.xml,去掉之前的滑動菜單,修改的頁面布局代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!--首頁面布局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg"
        android:fitsSystemWindows="true">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:navigationIcon="@drawable/icon_menu" />

    </LinearLayout>

    <!--滑動導航視圖-->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"      

不過就算是這樣,你目前還需要添加導航視圖的頭部視圖和菜單視圖。

在layout下建立一個nav_header.xml,作為導航的頭部視圖,正常的我們會在導航視圖裡面放置一些個人資訊,頭像,名稱等。

那麼頭像是一個圖檔,而且普遍是圓形圖檔,正常是通過一些第三方庫和自定義VIew來實作。還記得我們剛才導入的material庫嗎?可以用它裡面的控件來實作圓形頭像。

首先我們在styles.xml中增加如下代碼:

"circleImageStyle">
        <item name="cornerFamily">rounded</item>
        <item name="cornerSize">50%</item>
    </style>      

然後在nav_header.xml增加一個圖示控件和兩個文字控件,裡面代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="160dp"
    android:background="#1391F8"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <!--頭像-->
    <com.google.android.material.imageview.ShapeableImageView
        android:id="@+id/iv_avatar"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentBottom="true"
        android:layout_marginStart="24dp"
        android:layout_marginBottom="30dp"
        android:padding="1dp"
        android:layout_marginEnd="24dp"
        android:src="@mipmap/icon_default_avatar"
        app:shapeAppearanceOverlay="@style/circleImageStyle"
        app:strokeColor="#FFF"
        app:strokeWidth="2dp" />
    <!--名稱-->
    <TextView
        android:layout_marginTop="16dp"
        android:layout_alignTop="@+id/iv_avatar"
        android:id="@+id/tv_name"
        android:textSize="16sp"
        android:textColor="#FFF"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toEndOf="@+id/iv_avatar"
        android:text="初學者-Study" />
    <!--标簽-->
    <TextView
        android:layout_marginTop="8dp"
        android:id="@+id/tv_tip"
        android:textSize="14sp"
        android:layout_below="@+id/tv_name"
        android:textColor="#FFF"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toEndOf="@+id/iv_avatar"
        android:text="Android | Java"      

這裡有一個icon_default_avatar的圖示,也是我部落格的頭像,如下圖所示

Android 側滑抽屜菜單

那麼這個導航視圖的頭部就寫好了,下面來寫導航菜單。

在這之前能先放置五個圖示,都是通過路徑來繪制的。都放在drawable下。

icon_friend.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/black"
        android:pathData="M15,8c0,-1.42 -0.5,-2.73 -1.33,-3.76C14.09,4.1 14.53,4 15,4c2.21,0 4,1.79 4,4s-1.79,4 -4,4c-0.43,0 -0.84,-0.09 -1.23,-0.21c-0.03,-0.01 -0.06,-0.02 -0.1,-0.03C14.5,10.73 15,9.42 15,8zM16.66,13.13C18.03,14.06 19,15.32 19,17v3h4v-3C23,14.82 19.42,13.53 16.66,13.13zM9,4c2.21,0 4,1.79 4,4s-1.79,4 -4,4s-4,-1.79 -4,-4S6.79,4 9,4zM9,13c2.67,0 8,1.34 8,4v3H1v-3C1,14.34 6.33,13 9,13z"      

icon_wallet.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/black"
        android:pathData="M10,16V8c0,-1.1 0.89,-2 2,-2h9V5c0,-1.1 -0.9,-2 -2,-2H5C3.89,3 3,3.9 3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-1h-9C10.89,18 10,17.1 10,16zM13,8c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h8c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1H13zM16,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5S16.83,13.5 16,13.5z"      

icon_location.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/black"
        android:pathData="M13.02,20.77L13.02,20.77c0,0.64 0.59,1.13 1.21,0.99c1.12,-0.26 2.18,-0.7 3.12,-1.3c0.53,-0.34 0.61,-1.1 0.16,-1.55l0,0c-0.32,-0.32 -0.83,-0.4 -1.21,-0.16c-0.77,0.49 -1.62,0.85 -2.53,1.05C13.32,19.9 13.02,20.31 13.02,20.77z" />
    <path
        android:fillColor="@android:color/black"
        android:pathData="M4.03,12c0,-3.79 2.65,-6.97 6.2,-7.79c0.44,-0.1 0.75,-0.51 0.75,-0.96v0c0,-0.64 -0.6,-1.13 -1.22,-0.98C5.33,3.29 2.03,7.26 2.03,12c0,4.74 3.3,8.71 7.73,9.74c0.62,0.15 1.22,-0.34 1.22,-0.98v0c0,-0.46 -0.31,-0.86 -0.75,-0.96C6.68,18.97 4.03,15.79 4.03,12z" />
    <path
        android:fillColor="@android:color/black"
        android:pathData="M20.79,11L20.79,11c0.64,0 1.13,-0.59 0.99,-1.21c-0.26,-1.12 -0.7,-2.17 -1.3,-3.12c-0.34,-0.54 -1.1,-0.61 -1.55,-0.16l0,0c-0.32,0.32 -0.4,0.83 -0.15,1.21c0.49,0.76 0.85,1.61 1.05,2.53C19.92,10.7 20.33,11 20.79,11z" />
    <path
        android:fillColor="@android:color/black"
        android:pathData="M17.35,3.55c-0.95,-0.6 -2,-1.04 -3.12,-1.3c-0.62,-0.14 -1.21,0.35 -1.21,0.98v0c0,0.45 0.3,0.87 0.74,0.96c0.91,0.2 1.77,0.57 2.53,1.05c0.39,0.24 0.89,0.17 1.21,-0.16l0,0C17.96,4.64 17.89,3.89 17.35,3.55z" />
    <path
        android:fillColor="@android:color/black"
        android:pathData="M18.92,17.49L18.92,17.49c0.45,0.45 1.21,0.38 1.55,-0.16c0.6,-0.94 1.04,-2 1.3,-3.12c0.14,-0.62 -0.35,-1.21 -0.98,-1.21h0c-0.45,0 -0.87,0.3 -0.96,0.74c-0.2,0.91 -0.57,1.77 -1.05,2.53C18.52,16.66 18.6,17.17 18.92,17.49z" />
    <path
        android:fillColor="@android:color/black"
        android:pathData="M16,11.1C16,8.61 14.1,7 12,7s-4,1.61 -4,4.1c0,1.51 1.1,3.28 3.31,5.3c0.39,0.36 0.98,0.36 1.38,0C14.9,14.37 16,12.61 16,11.1zM12,12c-0.59,0 -1.07,-0.48 -1.07,-1.07c0,-0.59 0.48,-1.07 1.07,-1.07s1.07,0.48 1.07,1.07C13.07,11.52 12.59,12 12,12z"      

icon_phone.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
        android:tint="#000000"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0"
        android:width="24dp">
  <path android:fillColor="@android:color/white"
         android:pathData="M15.63,14.4l-2.52,2.5c-2.5,-1.43 -4.57,-3.5 -6,-6l2.5,-2.52c0.23,-0.24 0.33,-0.57 0.27,-0.9L9.13,3.8C9.04,3.34 8.63,3 8.15,3L4,3C3.44,3 2.97,3.47 3,4.03C3.17,6.92 4.05,9.63 5.43,12c1.58,2.73 3.85,4.99 6.57,6.57c2.37,1.37 5.08,2.26 7.97,2.43c0.56,0.03 1.03,-0.44 1.03,-1l0,-4.15c0,-0.48 -0.34,-0.89 -0.8,-0.98l-3.67,-0.73C16.2,14.07 15.86,14.17 15.63,14.4z"/>
</vector>      

icon_email.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
        android:tint="#000000"
        android:viewportHeight="24.0"
        android:viewportWidth="24.0"
        android:width="24dp">
  <path android:fillColor="@android:color/white"
         android:pathData="M20,4H4C2.9,4 2.01,4.9 2.01,6L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.1,4 20,4zM19.6,8.25l-7.07,4.42c-0.32,0.2 -0.74,0.2 -1.06,0L4.4,8.25C4.15,8.09 4,7.82 4,7.53c0,-0.67 0.73,-1.07 1.3,-0.72L12,11l6.7,-4.19C19.27,6.46 20,6.86 20,7.53C20,7.82 19.85,8.09 19.6,8.25z"/>
</vector>      

然後我們在res下建立一個menu檔案夾,檔案夾下建立一個nav_menu.xml檔案。裡面的代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/item_friend"
            android:icon="@drawable/icon_friend"
            android:title="朋友" />
        <item
            android:id="@+id/item_wallet"
            android:icon="@drawable/icon_wallet"
            android:title="錢包" />
        <item
            android:id="@+id/item_location"
            android:icon="@drawable/icon_location"
            android:title="位置" />
        <item
            android:id="@+id/item_phone"
            android:icon="@drawable/icon_phone"
            android:title="電話" />
        <item
            android:id="@+id/item_email"
            android:icon="@drawable/icon_email"
            android:title="郵箱"      

此時你會在預覽的地方看到這樣的畫面

Android 側滑抽屜菜單

不用擔心,圖示是有的,隻不過和使用方式有關系。下面我們回到這個activity_main.xml,把我們寫的導頭部和菜單都引入進NavigationView中。

Android 側滑抽屜菜單

運作一下吧。

Android 側滑抽屜菜單

這樣的效果如何呢?當然我們還需要與使用者互動才行,不然你就是中看不中用。

進入到MainActivity中,首先建立一個showMsg方法,用于彈出Toast提示。

/**
     * Toast提示
     * @param msg 内容
     */
    private void showMsg(String msg){
        Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
    }      

然後新增變量:

private NavigationView navView;//導航視圖      

然後在onCreate中綁定xml的id。

= findViewById(R.id.nav_view);      

再通過這個navView來擷取頭部視圖。

//擷取頭部視圖
    View headerView = navView.getHeaderView(0);      

頭部視圖中正常的頭像是有點選動作的了,那麼可以這樣寫:

//頭像點選
    headerView.findViewById(R.id.iv_avatar).setOnClickListener(v -> showMsg("頭像"));      

然後就是菜單視圖的點選了,如下所示,通過點選item的id進行判斷,然後提示,之後關閉滑動菜單。

//導航菜單點選
        navView.setNavigationItemSelectedListener(item -> {
            switch (item.getItemId()) {
                case R.id.item_friend:
                    showMsg("朋友");
                    break;
                case R.id.item_wallet:
                    showMsg("錢包");
                    break;
                case R.id.item_location:
                    showMsg("位置");
                    break;
                case R.id.item_phone:
                    showMsg("電話");
                    break;
                case R.id.item_email:
                    showMsg("郵箱");
                    break;
                default:
                    break;
            }
            //關閉滑動菜單
            drawerLayout.closeDrawer(GravityCompat.START);
            return true;
        });      

運作之後一一點選測試一下:

Android 側滑抽屜菜單

嗯,和預想的效果一緻,這也是現在很多APP側滑菜單的用法,基本上就差不多了。

五、菜單分類

假如上面的五個菜單是基礎功能,那麼下面再添加一個擴充菜單。

當然還是要先添加這個菜單圖示

icon_share.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M18,16c-0.79,0 -1.5,0.31 -2.03,0.81L8.91,12.7C8.96,12.47 9,12.24 9,12s-0.04,-0.47 -0.09,-0.7l7.05,-4.11C16.49,7.69 17.21,8 18,8c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3s-3,1.34 -3,3c0,0.24 0.04,0.48 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.05,4.12C15.04,18.53 15,18.76 15,19c0,1.66 1.34,3 3,3s3,-1.34 3,-3S19.66,16 18,16z"      

icon_send.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:autoMirrored="true"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M3,5.51v3.71c0,0.46 0.31,0.86 0.76,0.97L11,12l-7.24,1.81C3.31,13.92 3,14.32 3,14.78v3.71c0,0.72 0.73,1.2 1.39,0.92l15.42,-6.49c0.82,-0.34 0.82,-1.5 0,-1.84L4.39,4.58C3.73,4.31 3,4.79 3,5.51z"      

然後修改nav_menu.xml添加如下代碼:

android:title="擴充">
        <menu>
            <item
                android:id="@+id/item_share"
                android:icon="@drawable/icon_share"
                android:title="分享" />
            <item
                android:id="@+id/item_send"
                android:icon="@drawable/icon_send"
                android:title="發送"      

放在group的i下面,然後進入到MainActivity中,添加兩個菜單的點選事件

Android 側滑抽屜菜單

運作

Android 側滑抽屜菜單

你可以看到這裡還有分隔線。

六、動态菜單

  像這種導航菜單一般都是定好的,靜态的。但是保不齊就有需要動态的菜單,需要去動态改變一些資料。而動态的菜單就不能再去使用剛才的這種方式添加item了,我們可以用清單來解決。

說到清單你會想到ListView,不過現在都使用RecyclerView了。而為了簡化RecyclerView的使用,我打算引入幫助的庫,而為了模拟真實的接口傳回資料,也會使用一個Json解析庫。

下面首先在工程的下build.gradle的添加如下

//添加jitpack倉庫
        maven { url "https://jitpack.io" }      

添加位置如下圖:

Android 側滑抽屜菜單

為什麼要這麼做的呢?這裡你就要厘清楚Android依賴庫的由來,Google自己的庫和第三方庫。Google自己的庫在你建立項目時就已經添加了,如

google()
        jcenter()      

而第三方庫要想使用有些是需要添加jitpack倉庫的,也就是我上面添加的代碼。

下面進入app的build.gradle,在dependencies閉包{}中添加如下依賴庫:

//RecyclerView最好的擴充卡,讓你的擴充卡一目了然,告别代碼備援
    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
    //Gson庫
    implementation 'com.google.code.gson:gson:2.8.5'      

然後再點選Sync Now 進行同步,同步沒有報錯,則我們先來建構這個傳回的資料。

{
    "code":200,
    "data":[
        {
            "id":0,
            "name":"朋友"
        },
        {
            "id":1,
            "name":"錢包"
        },
        {
            "id":2,
            "name":"位置"
        },
        {
            "id":3,
            "name":"電話"
        },
        {
            "id":4,
            "name":"郵箱"
        },
        {
            "id":5,
            "name":"分享"
        },
        {
            "id":6,
            "name":"發送"
        }
    
    ],
    "msg":"Success"
}      

實際開發中的傳回資料和這個差不多,僅供參考。這一段JSON字元串,裡面有三個主體内容,code用來檢測你傳回的是否合格,通常200表示正常傳回,msg則表示描述,對于這個code的描述。data則是傳回的資料組數。下面通過這個傳回資料,我們可以寫出這樣的一個實體類。

在com.llw.drawerdemo中建立一個MenuResponse.java類,裡面的代碼如下:

package com.llw.drawerdemo;

import java.util.List;

/**
 * 菜單傳回資料
 * @author lonel
 */
public class MenuResponse {


    /**
     * code : 200
     * data : [{"id":0,"name":"朋友"},{"id":1,"name":"錢包"},{"id":2,"name":"位置"},{"id":3,"name":"電話"},{"id":4,"name":"郵箱"},{"id":5,"name":"分享"},{"id":6,"name":"發送"}]
     * msg : Success
     */

    private int code;
    private String msg;
    private List<DataBean> data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public List<DataBean> getData() {
        return data;
    }

    public void setData(List<DataBean> data) {
        this.data = data;
    }

    public static class DataBean {
        /**
         * id : 0
         * name : 朋友
         */

        private int id;
        private String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}      

然後再建立一個常量類Contanst.java,裡面的代碼如下:

package com.llw.drawerdemo;

/**
 * 常量
 * @author lonel
 */
public class Contanst {

    public static final int SUCCESS = 200;

    public static final String JSON = "{\n" +
            "    \"code\":200,\n" +
            "    \"data\":[\n" +
            "        {\n" +
            "            \"id\":0,\n" +
            "            \"name\":\"朋友\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\":1,\n" +
            "            \"name\":\"錢包\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\":2,\n" +
            "            \"name\":\"位置\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\":3,\n" +
            "            \"name\":\"電話\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\":4,\n" +
            "            \"name\":\"郵箱\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\":5,\n" +
            "            \"name\":\"分享\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"id\":6,\n" +
            "            \"name\":\"發送\"\n" +
            "        }\n" +
            "    \n" +
            "    ],\n" +
            "    \"msg\":\"Success\"\n" +
            "}";
}      

裡面是我們傳回的資料JSON字元串,用這種方式來模拟真實傳回資料。還有一點就是這個成功碼用一個全局的常量來表示,盡量不要再代碼中直接使用200來做為成功的傳回判定。用常量的好處就是改起來快,假如今天你是200為成功,明天變成100,那麼我隻需要改這個常量的值即可,而不需要你去每一個用200判定的地方都去手動改成100,效率上就提高很多。

下面來寫這個item的布局,在layout下建立一個item_menu.xml檔案,裡面的代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv_menu_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:foreground="?attr/selectableItemBackground"
    android:padding="16dp"
    android:textColor="#000"
    android:textSize="16sp"      

這裡的

android:foreground="?attr/selectableItemBackground"      

就是點選item的效果,體驗感更強一些。

然後去寫擴充卡,在com.llw.drawerdemo下建立一個MenuAdapter類,裡面的代碼如下:

package com.llw.drawerdemo;

import androidx.annotation.Nullable;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;

import java.util.List;


/**
 * 菜單清單擴充卡
 * @author lonel
 */
public class MenuAdapter extends BaseQuickAdapter<MenuResponse.DataBean, BaseViewHolder> {
    public MenuAdapter(int layoutResId, @Nullable List<MenuResponse.DataBean> data) {
        super(layoutResId, data);
    }

    @Override
    protected void convert(BaseViewHolder helper, MenuResponse.DataBean item) {
        helper.setText(R.id.tv_menu_name,item.getName());
        //添加點選事件
        helper.addOnClickListener(R.id.tv_menu_name);
    }
}      

相比于傳統的擴充卡,這樣寫就簡潔很多了,然後是修改布局,首先是activity_main.xml中,我們去掉nav_menu

Android 側滑抽屜菜單

保留這個headerLayout,然後去修改這個nav_header布局代碼:修改後如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:overScrollMode="never"
    android:orientation="vertical">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!--頭部菜單-->
        <RelativeLayout
            android:background="#1391F8"
            android:layout_width="match_parent"
            android:layout_height="160dp">
            <!--頭像-->
            <com.google.android.material.imageview.ShapeableImageView
                android:id="@+id/iv_avatar"
                android:layout_width="80dp"
                android:layout_height="80dp"
                android:layout_alignParentBottom="true"
                android:layout_marginStart="24dp"
                android:layout_marginEnd="24dp"
                android:layout_marginBottom="30dp"
                android:padding="1dp"
                android:src="@mipmap/icon_default_avatar"
                app:shapeAppearanceOverlay="@style/circleImageStyle"
                app:strokeColor="#FFF"
                app:strokeWidth="2dp" />
            <!--名稱-->
            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignTop="@+id/iv_avatar"
                android:layout_marginTop="16dp"
                android:layout_toEndOf="@+id/iv_avatar"
                android:text="初學者-Study"
                android:textColor="#FFF"
                android:textSize="16sp" />
            <!--标簽-->
            <TextView
                android:id="@+id/tv_tip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/tv_name"
                android:layout_marginTop="8dp"
                android:layout_toEndOf="@+id/iv_avatar"
                android:text="Android | Java"
                android:textColor="#FFF"
                android:textSize="14sp" />
        </RelativeLayout>

        <!--清單菜單-->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_menu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>

</androidx.core.widget.NestedScrollView>      

NestedScrollView表示一個滾動視圖,它裡面隻能放一個布局,當這個布局的高度超過螢幕時,則可以上下滾動顯示,而這個布局裡面又可以嵌套其他的布局。我在裡面放置了之前的相對布局和新增的清單控件。下面我們進入到MainActivity。

先聲明成員變量

private RecyclerView rvMenu;//清單      

然後在onCreate中通過headerView去綁定布局中的id。

//綁定清單控件
    rvMenu = headerView.findViewById(R.id.rv_menu);      

然後再寫一個方法用來顯示菜單清單。

/**
     * 顯示菜單清單
     */
    private void showMenuList() {
        //解析JSON資料
        MenuResponse menuResponse = new Gson().fromJson(Contanst.JSON, MenuResponse.class);
        if (menuResponse.getCode() == Contanst.SUCCESS) {
            //為空處理
            if(menuResponse.getData() ==null){
                showMsg("傳回菜單資料為空");
                return;
            }
            List<MenuResponse.DataBean> data = menuResponse.getData();
            //設定擴充卡的布局和資料源
            MenuAdapter menuAdapter = new MenuAdapter(R.layout.item_menu, data);
            //item點選事件
            menuAdapter.setOnItemChildClickListener((adapter, view, position) -> {
                    showMsg(data.get(position).getName());
                    //關閉滑動菜單
                    drawerLayout.closeDrawer(GravityCompat.START);
            });
            //設定線性布局管理器,預設是縱向
            rvMenu.setLayoutManager(new LinearLayoutManager(this));
            //設定擴充卡
            rvMenu.setAdapter(menuAdapter);
        } else {
            //錯誤提示
            showMsg(menuResponse.getMsg());
        }
    }      

裡面的代碼都有注釋,相信你一定可以看懂。

然後在onCreate中調用這個方法

Android 側滑抽屜菜單

運作。

Android 側滑抽屜菜單

效果是有了,但是好像沒有圖示有點不得勁是吧。因為實際開發中的圖示也是從背景傳回過來的,一般來說是一個網絡圖示位址,這個位址你可以通過Glide庫去進行圖示顯示。而我們沒有這個網絡位址,不過幸運的是,我們有之前手寫的七個圖示,不是嗎。我們将這七個圖示組成一個int數組,然後在擴充卡中進行配置就好了,不過首先呢需要改變一下item_menu.xml。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_menu"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:foreground="?attr/selectableItemBackground"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingStart="16dp">
    <!--菜單圖示-->
    <ImageView
        android:id="@+id/iv_menu_icon"
        android:layout_width="24dp"
        android:layout_height="24dp" />
    <!--菜單名稱-->
    <TextView
        android:layout_marginStart="16dp"
        android:id="@+id/tv_menu_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textColor="#000"
        android:textSize="16sp"      

然後進入MenuAdapter中,

新增一個圖示數組

private int[] iconArray = {R.drawable.icon_friend, R.drawable.icon_wallet, R.drawable.icon_location
            , R.drawable.icon_phone, R.drawable.icon_email, R.drawable.icon_share, R.drawable.icon_send};      

然後在convert中通過item的位置來擷取圖示數組中的圖示,然後設定到ImageVIew中,這樣寫是有弊端的,當你的資料條目和圖示數組長度不一緻時,就會出現數組越界,然後就報錯崩潰,程式閃退,是以實際中不會采取這種方式,我這裡隻是示範。其次我還改變了添加點選事件的圖示,之前是給TextView添加點選事件,現在是給LinearLayout添加點選事件。

Android 側滑抽屜菜單

然後我們回到MainActivity中,去給item添加分割線。

//添加item的分隔線
    rvMenu.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));      

添加位置如下:

Android 側滑抽屜菜單

下面再運作一次。

Android 側滑抽屜菜單

這裡菜單圖示有了,分割線也有了,不過實際為了美感,通常會去掉最後一個item的分隔線,這個可以通過自定義View來實作,網上多的是。繼承RecyclerView.ItemDecoration然後擷取item數量,最後一個item不繪制分割線。

我這裡就不詳細介紹這種方式了,我們可以用另一種巧妙的方式來解決:

添加靜态菜單。

下面再添加兩個圖示,

item_setting.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M18.04,7.99l-0.63,-1.4l-1.4,-0.63c-0.39,-0.18 -0.39,-0.73 0,-0.91l1.4,-0.63l0.63,-1.4c0.18,-0.39 0.73,-0.39 0.91,0l0.63,1.4l1.4,0.63c0.39,0.18 0.39,0.73 0,0.91l-1.4,0.63l-0.63,1.4C18.78,8.38 18.22,8.38 18.04,7.99zM21.28,12.72L20.96,12c-0.18,-0.39 -0.73,-0.39 -0.91,0l-0.32,0.72L19,13.04c-0.39,0.18 -0.39,0.73 0,0.91l0.72,0.32L20.04,15c0.18,0.39 0.73,0.39 0.91,0l0.32,-0.72L22,13.96c0.39,-0.18 0.39,-0.73 0,-0.91L21.28,12.72zM16.24,14.37l1.23,0.93c0.4,0.3 0.51,0.86 0.26,1.3l-1.62,2.8c-0.25,0.44 -0.79,0.62 -1.25,0.42l-1.43,-0.6c-0.2,0.13 -0.42,0.26 -0.64,0.37l-0.19,1.54c-0.06,0.5 -0.49,0.88 -0.99,0.88H8.38c-0.5,0 -0.93,-0.38 -0.99,-0.88L7.2,19.59c-0.22,-0.11 -0.43,-0.23 -0.64,-0.37l-1.43,0.6c-0.46,0.2 -1,0.02 -1.25,-0.42l-1.62,-2.8c-0.25,-0.44 -0.14,-0.99 0.26,-1.3l1.23,-0.93C3.75,14.25 3.75,14.12 3.75,14s0,-0.25 0.01,-0.37L2.53,12.7c-0.4,-0.3 -0.51,-0.86 -0.26,-1.3l1.62,-2.8c0.25,-0.44 0.79,-0.62 1.25,-0.42l1.43,0.6c0.2,-0.13 0.42,-0.26 0.64,-0.37l0.19,-1.54C7.45,6.38 7.88,6 8.38,6h3.23c0.5,0 0.93,0.38 0.99,0.88l0.19,1.54c0.22,0.11 0.43,0.23 0.64,0.37l1.43,-0.6c0.46,-0.2 1,-0.02 1.25,0.42l1.62,2.8c0.25,0.44 0.14,0.99 -0.26,1.3l-1.23,0.93c0.01,0.12 0.01,0.24 0.01,0.37S16.25,14.25 16.24,14.37zM13,14c0,-1.66 -1.34,-3 -3,-3s-3,1.34 -3,3s1.34,3 3,3S13,15.66 13,14z"      

icon_logout.xml

<?xml version="1.0" encoding="UTF-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:autoMirrored="true"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M5,5h6c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h6c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1H5V5z" />
    <path
        android:fillColor="@android:color/white"
        android:pathData="M20.65,11.65l-2.79,-2.79C17.54,8.54 17,8.76 17,9.21V11h-7c-0.55,0 -1,0.45 -1,1v0c0,0.55 0.45,1 1,1h7v1.79c0,0.45 0.54,0.67 0.85,0.35l2.79,-2.79C20.84,12.16 20.84,11.84 20.65,11.65z"      

然後修改之前的nav_menu.xml,修改後如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <!--增加其他菜單-->
    <item android:title="其他">
        <menu>
            <item
                android:id="@+id/item_setting"
                android:icon="@drawable/icon_settings"
                android:title="設定" />
            <item
                android:id="@+id/item_logout"
                android:icon="@drawable/icon_logout"
                android:title="退出"      

然後在styles.xml中增加一個item字型的大小設定

"MenuTextStyle">
        <item name="android:textSize">16sp</item>
    </style>      

再進入activity_main.xml,修改NavigationView,修改内容如下:

android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:theme="@style/MenuTextStyle"
        app:headerLayout="@layout/nav_header"
        app:itemIconSize="24dp"
        app:itemIconTint="#000"
        app:itemTextColor="#000"
        app:menu="@menu/nav_menu"      

再進入MainActivity中,修改菜單點選事件

Android 側滑抽屜菜單

然後運作一下:

Android 側滑抽屜菜單

嗯,這樣你現在動态也有靜态也有,挺好的,本文就寫到這裡了。

七、源碼