前言
DrawerLayout 是 Support Library 包中實現了側滑菜單效果的控件,可以說 DrawerLayout 是因爲第三方控件如 MenuDrawer 等的出現之後,google 借鑑而出現的產物。DrawerLayout 分爲側邊菜單和主內容區兩部分,側邊菜單可以根據手勢展開與隱藏(DrawerLayout 自身特性),主內容區的內容可以隨着菜單的點擊而變化。
一、DrawerLayout 基礎使用
DrawerLayout 其實是一個佈局控件,繼承 ViewGroup,與 LinearLayout 等控件是一種東西,屬於同級控件。但是 DrawerLayout 帶有滑動的功能。只要按照 DrawerLayout 的規定佈局方式寫完佈局,就能有側滑的效果。
DrawerLayout 最簡單的使用方式,添加 2 個子佈局,分別代表 APP 主頁面和側滑菜單頁面。
<?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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--主頁面-->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/meizi_2" />
<!--側滑菜單頁面-->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/pangzi" />
</androidx.drawerlayout.widget.DrawerLayout>
完成上面的佈局文件,就可以實現如下效果:
二、DrawerLayout + ToolBar 使用
日常開發中,每個界面都會有一個 ToolBar,並且側滑出來的內容都會在 ToolBar 的底部。最常見的就是 ToolBar 左側會有一個小圖標(號稱三道槓),在側滑菜單展示時,會加載一個動畫變成返回按鈕,完成這個效果不需要在 ToolBar 上自己添加 Icon,只需要藉助 ActionBarDrawerToggle 類就可實現。
ActionBarDrawerToggle 效果就是一個“三“ 然後點擊變”←“
ActionBarDrawerToggle 的作用:
- 改變 android.R.id.home 返回圖標
- DrawerLayout 拉出、隱藏,帶有 android.R.id.home 動畫效果
- 監聽 DrawerLayout 拉出、隱藏
因爲要實現側滑菜單在 ToolBar 底部,修改 XML 文件,將 ToolBar 放在 DrawerLayout 佈局外層。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:title="DrawerLayout"
tools:ignore="MissingConstraints" />
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/meizi_2" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/pangzi" />
</androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
// 設置左上角圖標["三" —— "←"]效果
ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
actionBarDrawerToggle.syncState();
drawerLayout.addDrawerListener(actionBarDrawerToggle);
三、NavigationView 介紹
NavigationView 是 Google 在側滑的 Material Design 的一種規範,所以提出了一個新的空間,用來規範側滑菜單的基本樣式。
對於抽屜式菜單界面很多 APP 都有應用,此前寫抽屜式界面都需要自定義。現在谷歌提供的導航視圖 NavigationView + DrawerLayout 結合使用,能提供很好的側滑交互體驗。
四、NavigationView 常用方法
方法 | 介紹 |
---|---|
addHeaderView(View view) | 將視圖添加爲導航菜單的標題 |
getHeaderCount() | 獲取此 NavigationView 中標頭的數量 |
getHeaderView(int index) | 獲取指定位置的標題視圖 |
getMenu() | 返回 Menu 與此導航視圖關聯的實例 |
inflateMenu(int resId) | 在此導航視圖中添加菜單資源 |
removeHeaderView(View view) | 刪除先前添加的標題視圖 |
setItemBackgroundResource(int resId) | 將菜單項的背景設置爲給定資源 |
setItemIconSize(int iconSize) | 設置用於菜單項圖標的大小(以像素爲單位) |
setItemTextAppearance(int resId) | 將菜單項的文本外觀設置爲給定資源 |
setItemTextColor(ColorStateList textColor) | 設置要在菜單項上使用的文本顏色 |
setItemIconTintList(ColorStateList tint) | 設置菜單項上使用 Icon 的顏色 |
setNavigationItemSelectedListener(NavigationView listener) | 設置一個偵聽器,當選擇菜單項時將用來通知該偵聽器 |
五、NavigationView 基礎使用
DrawerLayout + NavigationView + ToolBar 結合使用是項目中最常見的效果,通常有 2 種效果:
側滑菜單在 ToolBar 底部
側滑菜單沉浸式覆蓋 ToolBar 展示
第一種效果實際上就是在 XML 佈局中,將 ToolBar 佈局放到 DrawerLayout 外部,第二種效果是將 ToolBar 放到 DrawerLayout 內部主頁面佈局裏面,然後實現沉浸式效果。本文源碼中有沉浸式實現的工具類,感興趣的朋友可以下載源碼參考。
1、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/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:title="DrawerLayout"
app:titleTextColor="#FFF"
tools:ignore="MissingConstraints" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/meizi_2" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header_main"
app:insetForeground="@android:color/transparent"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
只需在 DrawerLayout 中添加 NavigationView 控件即可,其中介紹兩個屬性(也就是上圖中紅黃藍三個位置效果):
-
app:insetForeground="@android:color/transparent"
NavigationView 沉浸式展示 -
app:headerLayout="@layout/nav_header_main"
在 NavigationView 上添加一個 Header 佈局 -
app:menu="@menu/activity_main_drawer"
NavigationView 添加標籤 Item 的菜單
2、HeaderLayout 佈局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="200dp"
android:background="?attr/colorPrimary"
android:gravity="bottom"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<com.caobo.slideviewdemo.drawerlayout.MovingImageView
android:id="@+id/movingImageView"
android:layout_width="match_parent"
android:layout_height="250dp"
android:scaleType="centerCrop"
android:src="@mipmap/menu_header_background"
app:miv_load_on_create="false"
app:miv_max_relative_size="3.0"
app:miv_min_relative_offset="0.2"
app:miv_repetitions="-1"
app:miv_speed="100"
app:miv_start_delay="100" />
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="30dp"
android:paddingTop="16dp"
android:src="@mipmap/header_icon"
app:civ_border_color="@color/colorWhite"
app:civ_border_width="2dp" />
<TextView
android:id="@+id/tv_nick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="16dp"
android:paddingLeft="5dp"
android:text="Learn and live."
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textSize="18sp" />
</FrameLayout>
3、Menu 菜單文件
這裏的 icon 圖標全部使用 vector 矢量圖展示,如果還不會使用 Vector 矢量圖的朋友,可以參考:
Android Material Design Icon Genenerator 插件爲個人開發者提供 Icon 圖標大全
<?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/group_item_github"
android:icon="@drawable/ic_vector_github_grey"
android:title="項目主頁" />
<item
android:id="@+id/group_item_more"
android:icon="@drawable/ic_vector_more"
android:title="更多內容" />
<item
android:id="@+id/group_item_qr_code"
android:icon="@drawable/ic_vector_qr_code"
android:title="二維碼" />
<item
android:id="@+id/group_item_share_project"
android:icon="@drawable/ic_vector_share"
android:title="分享項目" />
</group>
<item android:title="選項">
<menu>
<item
android:id="@+id/item_model"
android:icon="@drawable/ic_vetor_setting"
android:title="夜間模式" />
<item
android:id="@+id/item_about"
android:icon="@drawable/ic_vector_about"
android:title="關於" />
</menu>
</item>
</menu>
4、Activity 代碼實現
NavigationView 的使用基本上都是 XML 文件中完成的,Activity 中不需要做太多處理,只需要 Activity 中添加 Menu 的監聽。本項目中使用了一個帶動畫的 ImageView,所以側滑菜單後,底部圖片會有動畫效果,感興趣的可以在底部下載源碼學習。
public class DrawerLayoutActivity extends BaseActivity {
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.drawerLayout)
DrawerLayout drawerLayout;
@BindView(R.id.navigationView)
NavigationView navigationView;
MovingImageView movingImageView;
private ActionBarDrawerToggle actionBarDrawerToggle;
@Override
protected void initView() {
movingImageView = navigationView.getHeaderView(0).findViewById(R.id.movingImageView);
// 設置左上角圖標["三" —— "←"]效果
actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
actionBarDrawerToggle.syncState();
drawerLayout.addDrawerListener(actionBarDrawerToggle);
// 設置不允許 NavigationMenuView 滾動
NavigationMenuView navigationMenuView = (NavigationMenuView) navigationView.getChildAt(0);
if (navigationMenuView != null) {
navigationMenuView.setVerticalScrollBarEnabled(false);
}
// NavigationView 監聽
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.group_item_github:
Toast.makeText(DrawerLayoutActivity.this,"項目主頁",Toast.LENGTH_SHORT).show();
break;
case R.id.group_item_more:
Toast.makeText(DrawerLayoutActivity.this,"更多內容",Toast.LENGTH_SHORT).show();
break;
case R.id.group_item_qr_code:
Toast.makeText(DrawerLayoutActivity.this,"二維碼",Toast.LENGTH_SHORT).show();
break;
case R.id.group_item_share_project:
Toast.makeText(DrawerLayoutActivity.this,"分享項目",Toast.LENGTH_SHORT).show();
break;
case R.id.item_model:
Toast.makeText(DrawerLayoutActivity.this,"夜間模式",Toast.LENGTH_SHORT).show();
break;
case R.id.item_about:
Toast.makeText(DrawerLayoutActivity.this,"關於",Toast.LENGTH_SHORT).show();
break;
}
item.setCheckable(false);
drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
});
}
@Override
protected int getLayoutResID() {
return R.layout.activity_drawerlayout;
}
}
六、NavigationView 全屏效果
1、設置全屏顯示
NavigationView 設置 android:layout_width="match_parent"
發現仍然無法實現全屏展示效果,如果想要 NavigationView 實現全屏展示,代碼中按照如下設置:
ViewGroup.LayoutParams mLayoutParams = navigationView.getLayoutParams();
int width = getResources().getDisplayMetrics().widthPixels;
mLayoutParams.width = width;
navigationView.setLayoutParams(mLayoutParams);
2、仿 QQ 側滑菜單效果
根據 NavigationView 全屏效果,模仿 QQ 個人中心側滑菜單效果,其實我們發現 QQ 這麼牛逼的軟件,也是使用 Google 原生控件實現,可想而知 Material Design 系列控件之強大。QQ 側滑菜單實現由以下兩點組成:
- QQ 側滑菜單鋪滿全屏
- 主頁面跟隨菜單一起滑動
第一點我們已經實現,只需要上面 4 行代碼就可以設置 NavigationView 全屏效果,接下來只需要將主頁面跟隨菜單一起滑動就可以實現效果,我們回想前面講到的 DrawerLayout.addDrawerListener()
方法,可以監聽事件,其中有 4 個回調方法:
-
onDrawerSlide(View drawerView, float slideOffset) 當抽屜的位置改變時調用
-
onDrawerOpened(View drawerView) 打開側滑界面觸發
-
onDrawerClosed(View drawerView) 關閉側滑界面觸發
-
onDrawerStateChanged(int newState) 狀態改變時觸發
根據上面 4 個回調方法,要實現 QQ 效果,需要在 onDrawerSlide(View drawerView, float slideOffset)
方法中進行處理,其中 slideOffset 返回的是抽屜菜單從隱藏到打開的偏移,取值 0~1,drawerView 就是側邊菜單佈局,具體實現代碼如下:
<?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"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/qq_1" />
<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/nav_header_main"
app:insetForeground="@android:color/transparent"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
// 主頁內容
View contentView = drawerLayout.getChildAt(0);
// 側邊欄
View menuView = drawerView;
// slideOffset 值默認是0~1
contentView.setTranslationX(menuView.getMeasuredWidth() * slideOffset);
}
@Override
public void onDrawerOpened(@NonNull View drawerView) {}
@Override
public void onDrawerClosed(@NonNull View drawerView) {}
@Override
public void onDrawerStateChanged(int newState) {}
});
提示:仿 QQ 側滑菜單效果,這裏沒有使用 ToolBar,直接截取了 2 張圖片展示。
七、手動控制菜單
默認手勢側滑就可以 Open 菜單,點擊空白處 Close。但是避免需要點擊事件觸發菜單狀態,DrawerLayout 設計非常人性化,提供了以下方法來實現:
-
openDrawer(View drawerView)
打開指定的摺疊項視圖,將其動畫到視圖中。
-
closeDrawer(View drawerView)
關閉指定的摺疊項視圖,將其動畫到視圖中。
-
closeDrawers()
關閉所有當前打開的抽屜視圖,通過動畫他們的視野。
使用舉例:
// 打開左邊菜單
mDrawerLayout.openDrawer(GravityCompat.START);
// 打開右邊菜單
mDrawerLayout.openDrawer(GravityCompat.END);
// 關閉左邊菜單6
mDrawerLayout.closeDrawer(GravityCompat.START);
// 關閉所有菜單
mDrawerLayout.closeDrawers();
源碼下載 源碼包含 Material Design 系列控件集合,定時更新,敬請期待!
八、總結
Android Material Design Library 推出了很長時間,越來越多的 APP 使用了符合 Library 包的控件,DrawerLayout 絕對是熱門之一,Material Design 定義了一個抽屜導航應該有何種外觀和感受,統一了側滑菜單和樣式。在 Android 原生手機上對 DrawerLayout+NavigationView 更是使用到了極致。
我的微信:Jaynm888
歡迎點評,
誠邀 Android 程序員加入微信交流羣
,公衆號回覆加羣或者加我微信邀請入羣。