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" />
</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">
<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" />
</LinearLayout>
<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" />
</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">
<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"/>
</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.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>
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">
<!-- Toolbar 放到 DrawerLayout 里 -->
<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" />
</FrameLayout>
</LinearLayout>
<!-- 侧边栏菜单 -->
<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" />
</androidx.drawerlayout.widget.DrawerLayout>
参考
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">
</android.support.v7.widget.Toolbar>
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"/>
</androidx.appcompat.widget.Toolbar>
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" />
</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>
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" />
</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">
<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>
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
由于当前正在忙业务需求,部分代码笔者只是粗略过了一下,目测是没有问题可以使用便参考其作者先放置在了这里,后续会更新自己测试后的代码。