DrawerLayout其實用了很久了,甚至封裝了一些代碼方便不同項目使用,但重構代碼的時候突然意識到這塊內容很不成體系,因此又參考了些文檔,組建了自己的一個文檔。
toolbar+drawerlayout能寫的效果很多,在此我也只是截取了一些從簡單到常用的寫法,其中由于toolbar繼承自viewgroup,所以可以實現很多自己想象中“這樣可以嗎”的效果。
DrawerLayout 是實現了側滑菜單效果的控件,分為側邊菜單和主內容區兩部分:
主內容區要放在側邊菜單前面,還有就是主內容區最好以 DrawerLayout 最好為界面的根布局,否則可能會出現觸摸事件被屏蔽的問題。
側滑菜單部分的布局必須設置 layout_gravity 屬性,表示側滑菜單是在左邊還是右邊,設置了
layout_gravity=“start/left” 的視圖才會被認為是側滑菜單。
使用的注意事項
主內容視圖一定要是 DrawerLayout 的第一個子視圖 主內容視圖寬度和高度需要 match_parent 必須顯示指定側滑視圖的
android:layout_gravity 屬性 android:layout_gravity = “start” 時,從左向右滑出菜單
android:layout_gravity = "end"時,從右向左滑出菜單 不推薦使用left和right!!!
側滑視圖的寬度以dp為單位,不建議超過320dp(為了總能看到一些主內容視圖)
設置側滑事件:mDrawerLayout.setDrawerListener(DrawerLayout.DrawerListene
參考
https://www.jianshu.com/p/082741fede64
1 最簡單的側滑 - 無圖標
<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"><FrameLayoutandroid:id="@+id/ly_content"android:layout_width="match_parent"android:layout_height="match_parent" /><ListViewandroid:id="@+id/list_left_drawer"android:layout_width="180dp"android:layout_height="match_parent"android:layout_gravity="start"android:background="#080808"android:choiceMode="singleChoice"android:divider="#FFFFFF"android:dividerHeight="1dp" /></android.support.v4.widget.DrawerLayout>
2 最簡單的側滑+右側圖標側滑
activity_main
<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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><includeandroid:id="@+id/topbar"layout="@layout/view_topbar"android:layout_width="wrap_content"android:layout_height="48dp" /><FrameLayoutandroid:id="@+id/fly_content"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout><fragmentandroid:id="@+id/fg_left_menu"android:name="jay.com.drawerlayoutdemo2.LeftFragment"android:layout_width="300dp"android:layout_height="match_parent"android:layout_gravity="start"android:tag="LEFT"tools:layout="@layout/fg_left" /><fragmentandroid:id="@+id/fg_right_menu"android:name="jay.com.drawerlayoutdemo2.RightFragment"android:layout_width="100dp"android:layout_height="match_parent"android:layout_gravity="end"android:tag="RIGHT"tools:layout="@layout/fg_right" /></android.support.v4.widget.DrawerLayout>
view_topbar
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#DCDEDB"><Buttonandroid:id="@+id/btn_right"android:layout_width="40dp"android:layout_height="40dp"android:layout_centerVertical="true"android:layout_alignParentRight="true"android:background="@drawable/btn_selctor"/></RelativeLayout>
上述兩個參考 DrawerLayout(官方側滑菜單)的簡單使用
https://www.runoob.com/w3cnote/android-tutorial-drawerlayout.html
3 左側官方圖標(toolbar)
3.1 側滑菜單在 ToolBar 底部
實現側滑菜單在 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.Toolbarandroid: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.DrawerLayoutandroid:id="@+id/drawerLayout"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/meizi_2" /><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/pangzi" /></androidx.drawerlayout.widget.DrawerLayout>
</LinearLayout>
java中使用ActionBarDrawerToggle定義效果
// 設置左上角圖標["三" —— "←"]效果
ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
actionBarDrawerToggle.syncState();
drawerLayout.addDrawerListener(actionBarDrawerToggle);
3.2 側滑菜單和 ToolBar 齊平
<?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"><!-- 主內容區 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!-- Toolbar 放到 DrawerLayout 里 --><androidx.appcompat.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="?attr/colorPrimary"app:title="DrawerLayout"app:titleTextColor="@android:color/white" /><!-- 主要內容區域 --><FrameLayoutandroid:id="@+id/contentFrame"android:layout_width="match_parent"android:layout_height="match_parent"><!-- 示例背景圖 --><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/pangzi" /></FrameLayout></LinearLayout><!-- 側邊欄菜單 --><ListViewandroid:id="@+id/list_left_drawer"android:layout_width="240dp"android:layout_height="match_parent"android:layout_gravity="start"android:background="#111"android:choiceMode="singleChoice"android:divider="#FFFFFF"android:dividerHeight="1dp" /></androidx.drawerlayout.widget.DrawerLayout>
參考
https://juejin.cn/post/6850418119106789384
4 自定義圖標(toolbar)
4.1 xml中Toolbar根節點設置圖標
<android.support.v7.widget.Toolbarapp:navigationIcon="@drawable/ic_add_follow"android:id="@+id/tool_bar"android:layout_width="match_parent"android:layout_height="50dp"android:background="@android:color/holo_green_light"></android.support.v7.widget.Toolbar>
public class TextActivity extends AppCompatActivity {private ActionBarDrawerToggle toggle;private ImageView toolBarIcon;private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.text_tool_bar);initToolBar();}private void initToolBar() {Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);//不顯示標題toolbar.setTitle(""); setSupportActionBar(toolbar); //把開關和DrawerLayout關聯toggle = new ActionBarDrawerToggle(this, mDrawerLayout, 0, 0);}//覆寫方法讓系統判斷點擊的圖標后是否彈出側拉頁面@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()){case android.R.id.home:toggle.onOptionsItemSelected(item);}return super.onOptionsItemSelected(item);}
弊端:一:這種方式雖然可以在布局文件中來設置圖標,但是無法給圖標設置選擇器
二:由于是在ToolBar的根節點來設置圖片,所以不能只當圖片擺放的位置
優點:直接在XML中指定圖片,而且一行代碼搞定
4.2 xml中在ToolBar里面來設置子控件來自定義圖標
ToolBar繼承自ViewGroup,完全可以用來盛放控件
<androidx.appcompat.widget.Toolbarandroid:id="@+id/tool_bar"android:layout_width="match_parent"android:layout_height="50dp" app:contentInsetStart="0.0dp"android:background="@android:color/holo_green_light"><ImageViewandroid:layout_gravity="left"android:id="@+id/tool_bar_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher" android:background="@drawable/selector_infodetail_back_bg"/>
</androidx.appcompat.widget.Toolbar>
app:contentInsetStart=“0.0dp” 控制起始位置的內容內邊距,在toolbar想盛放不只是菜單欄時十分重要。
java
public class TextActivity extends AppCompatActivity {private ActionBarDrawerToggle toggle;private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.text_tool_bar);initToolBar();}private void initToolBar() {//找到圖標的idImageView toolBarIcon = (ImageView) findViewById(R.id.tool_bar_icon);Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);toolbar.setTitle(""); setSupportActionBar(toolbar);//設置監聽toolBarIcon.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {toggle();}});}}
private void toggle() {int drawerLockMode = mDrawerLayout.getDrawerLockMode(GravityCompat.START);if (mDrawerLayout.isDrawerVisible(GravityCompat.START)&& (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {mDrawerLayout.closeDrawer(GravityCompat.START);} else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {mDrawerLayout.openDrawer(GravityCompat.START);}}
通過這種方式設置的圖標就不能通過覆寫onOptionsItemSelected方法的方式來實現側拉頁面的打開和關閉了,因為圖標的id已經不是android.R.id.home,所以只能寫監聽事件來完成側拉頁面的打開和關閉。
通過查看onOptionsItemSelected的源碼發現系統內部實現方式最終調用的是toggle方法,但是這個方法是私有的我們不能通過對象調用到,發現這個方法中只用到了DrawerLayout的對象,所以就直接將這個方法拷貝到自己的類中來使用,完成了這個效果
弊端:XML中代碼比較多
優點:可以給圖標設置狀態選擇器,圖標的擺放位置比較靈活,還可以放其他的控件
4.3 java中設置toolbar圖標(actionbar版本)
actionbar版本
public class TempActivity extends AppCompatActivity {ActionBarDrawerToggle toggle;private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.text_tool_bar);initToolBar();}private void initToolBar() {Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);//設置圖標toolbar.setNavigationIcon(R.drawable.ic_launcher);// 標題toolbar.setTitle("Title");//把ToolBar的設置的ActionBar的位置setSupportActionBar(toolbar);//獲取開關同時讓開關和DrawerLayout關聯在一起toggle = new ActionBarDrawerToggle(this, mDrawerLayout, 0, 0);getSupportActionBar().setDisplayHomeAsUpEnabled(true);//設置點擊事件,點擊彈出menu界面mDrawerLayout.setDrawerListener(toggle);}//覆寫方法讓系統判斷點擊的圖標后是否彈出側拉頁面@Overridepublic boolean onOptionsItemSelected(MenuItem item) {toggle.onOptionsItemSelected(item);return super.onOptionsItemSelected(item);}
}
這樣就把左側的圖標設置成了我們需要的圖標,同時點擊圖標也可以彈出DrawerLayout的側拉頁面,但是注意:在以上的代碼中少了一行代碼toggle.syncState();作用是將左側小圖標和側拉頁面的狀態同步,只有當去掉這一行代碼的時候,這個方法自定義的圖標才會顯示
弊端:1.使用代碼來放置圖標沒有XML靈活,
2.這種方式不能給圖標是指背景選擇器
上述三種方法參考
https://www.cnblogs.com/zhujiabin/p/7530930.html
4.4 java中設置toolbar圖標(無actionbar)
筆者一般使用這種,個人覺得更靈活一些
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("");
toolbar.setNavigationIcon(R.drawable.menu_ic);DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
//監聽打開和關閉
toolbar.setNavigationOnClickListener(v -> {if (drawerLayout.isDrawerOpen(GravityCompat.START)) {drawerLayout.closeDrawer(GravityCompat.START);} else {drawerLayout.openDrawer(GravityCompat.START);}
});
5 DrawerLayout + NavigationView + ToolBar 結合使用
仍然是
側滑菜單在 ToolBar 底部
側滑菜單沉浸式覆蓋 ToolBar 展示
第一種效果實際上就是在 XML 布局中,將 ToolBar 布局放到 DrawerLayout 外部,第二種效果是將 ToolBar 放到 DrawerLayout 內部主頁面布局里面,然后實現沉浸式效果。本文源碼中有沉浸式實現的工具類,感興趣的朋友可以下載源碼參考。
下面以沉浸式為例
<?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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><androidx.appcompat.widget.Toolbarandroid: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" /><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/meizi_2" /></LinearLayout><com.google.android.material.navigation.NavigationViewandroid: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>
nav_header_main
<?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.MovingImageViewandroid: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.CircleImageViewandroid: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" /><TextViewandroid: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>
activity_main_drawer
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><group android:checkableBehavior="single"><itemandroid:id="@+id/group_item_github"android:icon="@drawable/ic_vector_github_grey"android:title="項目主頁" /><itemandroid:id="@+id/group_item_more"android:icon="@drawable/ic_vector_more"android:title="更多內容" /><itemandroid:id="@+id/group_item_qr_code"android:icon="@drawable/ic_vector_qr_code"android:title="二維碼" /><itemandroid:id="@+id/group_item_share_project"android:icon="@drawable/ic_vector_share"android:title="分享項目" /></group><item android:title="選項"><menu><itemandroid:id="@+id/item_model"android:icon="@drawable/ic_vetor_setting"android:title="夜間模式" /><itemandroid:id="@+id/item_about"android:icon="@drawable/ic_vector_about"android:title="關于" /></menu></item>
</menu>
java代碼
NavigationView 的使用基本上都是 XML 文件中完成的,Activity 中不需要做太多處理,只需要 Activity 中添加 Menu 的監聽。
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;@Overrideprotected 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() {@Overridepublic 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;}});}@Overrideprotected int getLayoutResID() {return R.layout.activity_drawerlayout;}
}
xml中只需在 DrawerLayout 中添加 NavigationView 控件即可,其中介紹兩個屬性(也就是上圖中紅黃藍三個位置效果):
app:insetForeground="@android:color/transparent" NavigationView 沉浸式展示
app:headerLayout="@layout/nav_header_main" 在 NavigationView 上添加一個 Header 布局
app:menu="@menu/activity_main_drawer" NavigationView 添加標簽 Item 的菜單
上述參考
https://juejin.cn/post/6850418119106789384
結語
//todo
由于當前正在忙業務需求,部分代碼筆者只是粗略過了一下,目測是沒有問題可以使用便參考其作者先放置在了這里,后續會更新自己測試后的代碼。