Android DrawerLayout NavigationView实现固定底部退出的可展开菜单
下面是不依赖第三方库,基于AndroidX和Material Design组件的实现方案,完全匹配你需要的菜单结构:
一、重构菜单XML结构
按照你期望的MAIN、SPECIAL分组,以及底部固定退出项的结构,修改res/menu/activity_main_drawer.xml:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <!-- MAIN 分组 --> <group android:id="@+id/group_main" android:orderInCategory="10"> <item android:id="@+id/category_1" android:title="Category 1" android:icon="@android:drawable/ic_menu_add"> <menu> <group android:checkableBehavior="single"> <item android:id="@+id/item_1" android:title="item 1" android:icon="@android:drawable/ic_menu_call"/> <item android:id="@+id/item_2" android:title="item 2" android:icon="@android:drawable/ic_menu_call"/> <item android:id="@+id/item_3" android:title="item 3" android:icon="@android:drawable/ic_menu_call"/> </group> </menu> </item> </group> <!-- SPECIAL 分组 --> <group android:id="@+id/group_special" android:orderInCategory="20"> <item android:id="@+id/category_2" android:title="Category 2" android:icon="@android:drawable/ic_menu_add"> <menu> <group android:checkableBehavior="single"> <item android:id="@+id/item_5" android:title="item 5" android:icon="@android:drawable/ic_menu_call"/> <item android:id="@+id/item_6" android:title="item 6" android:icon="@android:drawable/ic_menu_call"/> <item android:id="@+id/item_7" android:title="item 7" android:icon="@android:drawable/ic_menu_call"/> </group> </menu> </item> </group> <!-- 底部固定退出项,高order值确保排在最底部 --> <group android:id="@+id/group_footer" android:orderInCategory="1000"> <item android:id="@+id/nav_logout" android:title="退出" android:icon="@android:drawable/ic_menu_close_clear_cancel"/> </group> </menu>
二、实现菜单展开/折叠与点击逻辑
NavigationView原生支持子菜单的展开/折叠,但需要手动切换父项图标。修改MainActivity代码,替换默认的NavigationUI绑定逻辑,自定义点击监听:
package com.example.myapplication; import android.os.Bundle; import android.view.MenuItem; import android.view.View; import android.view.Menu; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.navigation.NavigationView; import androidx.annotation.NonNull; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.AppCompatActivity; import com.example.myapplication.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private AppBarConfiguration mAppBarConfiguration; private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.appBarMain.toolbar); binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); DrawerLayout drawer = binding.drawerLayout; NavigationView navigationView = binding.navView; // 配置AppBar,只包含实际的页面目标 mAppBarConfiguration = new AppBarConfiguration.Builder( R.id.item_1, R.id.item_2, R.id.item_3, R.id.item_5, R.id.item_6, R.id.item_7) .setOpenableLayout(drawer) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration); // 自定义导航菜单点击事件 navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { int id = item.getItemId(); // 处理可展开的父菜单 if (id == R.id.category_1 || id == R.id.category_2) { // 切换子菜单显示状态 item.setSubmenuVisible(!item.isSubmenuVisible()); // 切换展开/折叠图标 item.setIcon(item.isSubmenuVisible() ? R.drawable.ic_expand_less : R.drawable.ic_expand_more); drawer.closeDrawers(); return true; } // 处理退出项 else if (id == R.id.nav_logout) { // 这里添加你的退出逻辑,比如退出登录、关闭页面等 drawer.closeDrawers(); return true; } // 处理子菜单项导航 else { NavigationUI.onNavDestinationSelected(item, navController); drawer.closeDrawers(); return true; } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onSupportNavigateUp() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); return NavigationUI.navigateUp(navController, mAppBarConfiguration) || super.onSupportNavigateUp(); } }
注意:需要在res/drawable目录下添加展开(ic_expand_more.xml)和折叠(ic_expand_less.xml)的矢量图标,可直接从Material Icons库获取。
三、优化底部固定项(可选)
如果希望退出项完全固定在导航栏底部,不受菜单滚动影响,可以改用NavigationView的footerLayout属性:
- 创建底部布局
res/layout/nav_footer_logout.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp" android:background="?attr/colorSurface"> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="?attr/colorOutline"/> <TextView android:id="@+id/tv_logout" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="退出" android:drawableStart="@android:drawable/ic_menu_close_clear_cancel" android:drawablePadding="16dp" android:paddingVertical="12dp" android:textSize="16sp"/> </LinearLayout>
- 修改NavigationView布局,添加
app:footerLayout属性,并移除菜单里的退出项:
<com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" app:footerLayout="@layout/nav_footer_logout" />
- 在Activity里给底部退出项添加点击事件:
// 在onCreate中添加 View footerLayout = navigationView.getFooterView(0); footerLayout.findViewById(R.id.tv_logout).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 处理退出逻辑 drawer.closeDrawers(); } });
四、清理冗余代码
移除你之前在NavigationView内部嵌套的RecyclerView,以及Activity中相关的RecyclerView初始化代码,因为NavigationView本身已经承载菜单内容,不需要额外列表。
内容的提问来源于stack exchange,提问作者Feroz Siddiqui
相关产品推荐
相关产品推荐

