android抽屉DrawerLayout在2025的沉浸式兼容

目前使用View的DrawerLayout+NavigationView实现抽屉似乎已经年久失修。官方已经逐渐转向compose的Drawer。本文暂时没有使用compose。

基本使用

布局如下:基本逻辑为DrawerLayout+底部你的主界面+一层遮罩+navigationView。

navigationView可以使用headerLayout和menu。自行查阅。这里我自定义一行title+recyclerView+底部登录设置样式。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:openDrawer="start">

    <!-- 主屏幕显示内容 -->
    <include
        android:id="@+id/content"
        layout="@layout/activity_drawer_layout_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <View
        android:id="@+id/mask"
        android:alpha="0"
        android:background="#80000000"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <!-- 侧滑菜单显示内容 -->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav"
        app:drawerLayoutCornerSize="0dp"
        android:layout_gravity="start"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="48dp">
                <TextView
                    android:text="DrawerLayout-Tester"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"/>
            </androidx.constraintlayout.widget.ConstraintLayout>

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rcv"
                android:layout_weight="1"
                android:layout_width="match_parent"
                android:layout_height="0dp"/>

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="64dp">
                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/headIcon"
                    app:layout_constraintTop_toTopOf="parent"
                    android:layout_marginStart="@dimen/ui_padding_edge"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    android:src="@drawable/ic_floating_wrap"
                    android:tint="#111111"
                    android:layout_width="48dp"
                    android:layout_height="48dp"/>

                <TextView
                    android:text="用户abc123"
                    android:textSize="18sp"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintStart_toEndOf="@id/headIcon"
                    android:layout_marginStart="8dp"
                    app:layout_constraintBottom_toBottomOf="parent"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"/>

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/ivTag"
                    app:layout_constraintTop_toTopOf="parent"
                    android:layout_marginEnd="8dp"
                    app:layout_constraintEnd_toStartOf="@id/ivMenu"
                    app:layout_constraintBottom_toBottomOf="parent"
                    android:src="@drawable/ic_floating_tag"
                    android:tint="#111111"
                    android:layout_width="36dp"
                    android:layout_height="36dp"/>

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/ivMenu"
                    app:layout_constraintTop_toTopOf="parent"
                    android:layout_marginEnd="@dimen/ui_padding_edge"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintBottom_toBottomOf="parent"
                    android:src="@drawable/ic_floating_menu"
                    android:tint="#111111"
                    android:layout_width="36dp"
                    android:layout_height="36dp"/>
            </androidx.constraintlayout.widget.ConstraintLayout>
        </LinearLayout>
    </com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>

上图为目标。

于是就开始踩坑了。

不要乱加fitsSystemWindows

android:fitsSystemWindows="true" 会出现一个title栏。 不好。

主题圆角问题

如果你的activity或者application使用Theme.Material3.Light.NoActionBar 主题,抽屉打开就是圆角效果。

使用MaterialComponents(M2)就没有圆角。

可以在 NavigationView上添加 app:drawerLayoutCornerSize="0dp" 去除圆角。

沉浸式处理

现在android最难兼容的就是沉浸式。

通过如下代码获取statusBar和navBar的高度进行padding操作。

kotlin 复制代码
 rootDrawer.post {
            if (rootDrawer.isAttachedToWindow) {
                val heights = this@AgentChatActivity.currentStatusBarAndNavBarHeight()
                heights?.let { pair->
                    val statusBarHeight = pair.first
                    val navBarHeight = pair.second

                    navigationView.updatePadding(top = statusBarHeight, bottom = navBarHeight)
                    navigationView.layoutParams = navigationView.layoutParams.apply {
                        width = rootDrawer.width * 2 / 3
                    }
                }
            }
        }
fun Activity.currentStatusBarAndNavBarHeight() : Pair<Int, Int>? {
    val insets = ViewCompat.getRootWindowInsets(window.decorView) ?: return null
    val nav = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
    val sta = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
    return sta to nav
}

沉浸式后出现navigationView上方StatusBar遮罩

xml 复制代码
<!-- 侧滑菜单显示内容 追加layout_marginTop="0.1dp" 解决status bar背景overlay-->
<com.google.android.material.navigation.NavigationView
    android:id="@+id/navigationView"
    android:layout_gravity="start"
    android:layout_marginTop="0.1dp"

发现侧滑过来,抽屉的上方被statusBar遮挡。通过尝试,给出一点点marginTop就能解决。谁知道什么原因呢。

实现推拉的效果

很多app有左右推拉效果,比如腾讯元宝,DeepSeek。

第一步移除自带的黑色遮罩 drawer.setScrimColor(Color.TRANSPARENT)

第二步 addDrawerListener mask+translation实现右侧跟随

kotlin 复制代码
		rootDrawer.addDrawerListener(object : DrawerListener {
            override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
                // slideOffset: 0(完全关闭)到 1(完全打开)
                // 计算主内容应该平移的距离:抽屉宽度 * 滑动比率
                val moveX = drawerView.width * slideOffset
                chatLayout.mask.alpha = slideOffset * 0.5f

                // 如果是左侧抽屉,主内容应向右平移
                if (drawerView.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
                    chatLayout.translationX = moveX
                } else {
                    chatLayout.translationX = -moveX
                }
            }

            override fun onDrawerOpened(drawerView: View) {
            }

            override fun onDrawerClosed(drawerView: View) {
                // 抽屉关闭后,将主内容复位
                chatLayout.translationX = 0f
                // 清除遮罩
                chatLayout.mask.alpha = 0f
            }

            override fun onDrawerStateChanged(newState: Int) {}
        })

			//沉浸式处理
       

屏幕内左滑

主要就是实现在主界面上左滑能触发左边的抽屉拉出。

如果你的主界面很复杂,有一些嵌套的左滑操作就不要搞这个。

参考:

https://github.com/PureWriter/FullDraggableDrawer

实现逻辑就是touch事件检测,浏览了下代码中写了不少忽略的逻辑,避免与主界面冲突减少误触的风险。

相关推荐
2501_944424126 小时前
Flutter for OpenHarmony游戏集合App实战之贪吃蛇食物生成
android·开发语言·flutter·游戏·harmonyos
2501_937145419 小时前
神马影视8.8版2026最新版:核心技术升级与多场景适配解析
android·源码·电视盒子·源代码管理
2501_9444241210 小时前
Flutter for OpenHarmony游戏集合App实战之俄罗斯方块七种形状
android·开发语言·flutter·游戏·harmonyos
不会Android的潘潘12 小时前
受限系统环境下的 WebView 能力演进:车载平台 Web 渲染异常的根因分析与优化实践
android·java·前端·aosp
建军啊12 小时前
java web常见lou洞
android·java·前端
豆奶dudu12 小时前
安卓应用签名生成+微信开放平台安卓应用签名
android·微信开放平台
AC赳赳老秦13 小时前
Dify工作流+DeepSeek:运维自动化闭环(数据采集→报告生成)
android·大数据·运维·数据库·人工智能·golang·deepseek
2501_9444241214 小时前
Flutter for OpenHarmony游戏集合App实战之记忆翻牌配对消除
android·java·开发语言·javascript·windows·flutter·游戏
2501_9445264214 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 设置功能实现
android·javascript·flutter·游戏·harmonyos
冬奇Lab14 小时前
【Kotlin系列11】协程原理与实战(下):Flow与Channel驯服异步数据流
android·开发语言·kotlin