如何开发实现Gmail安卓App同款侧边菜单栏动画效果
Gmail安卓同款侧边栏实现方案
核心使用组件
不需要同时嵌套NavigationRail和NavigationDrawer两个组件,堆叠两层视图本身就会导致过渡卡顿。Gmail的侧边栏本质是基于DrawerLayout+NavigationView自定义状态切换逻辑实现的单容器组件,同时兼顾NavigationRail的折叠态外观和NavigationDrawer的展开动画效果。
具体实现步骤
- 基础布局搭建:根布局使用
DrawerLayout,仅保留一个侧边栏容器,默认设置为折叠态宽度(通常为72dp,和标准NavigationRail宽度一致),展开态宽度设置为Material规范的侧边栏宽度(320dp,可根据屏幕尺寸自适应调整)。禁用DrawerLayout默认的滑入滑出手势和动画,避免和自定义动画冲突。 - 统一动画驱动:使用同一个
ValueAnimator控制整个过渡过程,所有属性变化绑定同一动画进度,保证过渡一致性。展开时动画同步执行三个变化:侧边栏宽度从72dp过渡到320dp、菜单项文字透明度从0渐变到1、菜单项图标从居中位置偏移到左侧位置;折叠时反向执行上述变化。 - 状态管理:仅维护
EXPANDED、COLLAPSED两种侧边栏状态,点击汉堡按钮、侧边栏滑动手势都直接触发状态切换,调用上述动画逻辑即可。 - 手势适配:如果需要保留NavigationDrawer的边缘滑入效果,监听主内容区的边缘滑动事件,直接触发侧边栏展开动画,不需要额外弹出抽屉层。
关键代码示例
布局文件示例
<androidx.drawerlayout.widget.DrawerLayout android:id="@+id/drawerLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 主内容区域 --> <FrameLayout android:id="@+id/mainContent" android:layout_width="match_parent" android:layout_height="match_parent"/> <!-- 自定义侧边栏容器 --> <LinearLayout android:id="@+id/sidebarContainer" android:layout_width="72dp" android:layout_height="match_parent" android:layout_gravity="start" android:background="?attr/colorSurface" android:orientation="vertical"> <com.google.android.material.navigation.NavigationView android:id="@+id/navigationView" android:layout_width="match_parent" android:layout_height="match_parent" app:menu="@menu/sidebar_menu" app:itemIconGravity="start"/> </LinearLayout> </androidx.drawerlayout.widget.DrawerLayout>
状态切换动画代码
// 初始化时先禁用DrawerLayout默认抽屉行为 drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) private fun toggleSidebar(isExpand: Boolean) { val collapsedWidth = resources.getDimensionPixelSize(R.dimen.sidebar_collapsed_width) // 72dp val expandedWidth = resources.getDimensionPixelSize(R.dimen.sidebar_expanded_width) // 320dp val targetWidth = if (isExpand) expandedWidth else collapsedWidth ValueAnimator.ofInt(sidebarContainer.width, targetWidth).apply { duration = 300 // 和Material Design默认抽屉动画时长保持一致 interpolator = FastOutSlowInInterpolator() addUpdateListener { anim -> val progress = anim.animatedFraction // 更新侧边栏宽度 sidebarContainer.layoutParams.width = anim.animatedValue as Int sidebarContainer.requestLayout() // 同步更新菜单项文字透明度 for (i in 0 until navigationView.menu.size()) { val menuItem = navigationView.menu.getItem(i) // 这里可以根据需求额外调整图标偏移量 menuItem.titleCondensed = menuItem.title navigationView.findViewById<TextView>(R.id.design_menu_item_text)?.alpha = if (isExpand) progress else 1 - progress } } start() } }
内容的提问来源于stack exchange,提问作者aeroxr1
相关产品推荐
相关产品推荐

