Android补全计划 DrawerLayout使用

发布于:2025-07-27 ⋅ 阅读:(16) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到