> 技术文档 > Android补全计划 DrawerLayout使用

Android补全计划 DrawerLayout使用

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\"> <FrameLayout android:id=\"@+id/ly_content\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" /> <ListView android: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\" />

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\"> <LinearLayout android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" android:orientation=\"vertical\"> <include android:id=\"@+id/topbar\" layout=\"@layout/view_topbar\" android:layout_width=\"wrap_content\" android:layout_height=\"48dp\" /> <FrameLayout android:id=\"@+id/fly_content\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" />  <fragment android: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\" /> <fragment android: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\" /> 

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\"> <Button android: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\"/>

上述两个参考 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.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\" /> 

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\">  <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:title=\"DrawerLayout\" app:titleTextColor=\"@android:color/white\" />  <FrameLayout android:id=\"@+id/contentFrame\" 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/pangzi\" />    <ListView android: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\" />

参考
https://juejin.cn/post/6850418119106789384

4 自定义图标(toolbar)

4.1 xml中Toolbar根节点设置图标

<android.support.v7.widget.Toolbar app: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\"> 
public class TextActivity extends AppCompatActivity { private ActionBarDrawerToggle toggle; private ImageView toolBarIcon; private DrawerLayout mDrawerLayout; @Override protected 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); } //覆写方法让系统判断点击的图标后是否弹出侧拉页面 @Override public 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.Toolbar android:id=\"@+id/tool_bar\" android:layout_width=\"match_parent\" android:layout_height=\"50dp\" app:contentInsetStart=\"0.0dp\" android:background=\"@android:color/holo_green_light\"> <ImageView android: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\"/>

app:contentInsetStart=“0.0dp” 控制起始位置的内容内边距,在toolbar想盛放不只是菜单栏时十分重要。
java

public class TextActivity extends AppCompatActivity { private ActionBarDrawerToggle toggle; private DrawerLayout mDrawerLayout; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.text_tool_bar); initToolBar(); } private void initToolBar() { //找到图标的id ImageView 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() { @Override public 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; @Override protected 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); } //覆写方法让系统判断点击的图标后是否弹出侧拉页面 @Override public 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\"> <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\" />  <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\" />

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.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\" />

activity_main_drawer

<?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=\"分享项目\" />  <item android:title=\"选项\">  <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=\"关于\" />  

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; @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; }}

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
由于当前正在忙业务需求,部分代码笔者只是粗略过了一下,目测是没有问题可以使用便参考其作者先放置在了这里,后续会更新自己测试后的代码。