You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android DrawerLayout NavigationView实现固定底部退出的可展开菜单

在Android原生实现带可展开菜单和底部固定退出项的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属性:

  1. 创建底部布局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>
  1. 修改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" />
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 02:57:53