如何为Android应用的多个Activity添加Navigation Drawer
你选择用BaseActivity来统一管理导航抽屉的思路非常棒,这是Android开发中复用UI组件的经典方案,能有效避免在每个Activity里重复写抽屉的初始化和逻辑代码。下面我结合你提到的BaseActivity和布局文件,给你完整的实现方案和注意事项:
1. 编写BaseActivity核心代码
首先创建BaseActivity.java,它作为所有需要导航抽屉的Activity的父类,负责初始化抽屉、处理菜单点击等通用逻辑:
public class BaseActivity extends AppCompatActivity { protected DrawerLayout drawerLayout; protected NavigationView navigationView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 加载基础布局 setContentView(R.layout.activity_base); // 初始化抽屉组件 drawerLayout = findViewById(R.id.drawer_layout); navigationView = findViewById(R.id.nav_view); // 设置ActionBar的抽屉开关按钮 ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawerLayout, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawerLayout.addDrawerListener(toggle); toggle.syncState(); // 启用ActionBar的返回按钮(抽屉开关) getSupportActionBar().setDisplayHomeAsUpEnabled(true); // 设置抽屉菜单的点击事件 setupNavigationMenu(); } private void setupNavigationMenu() { navigationView.setNavigationItemSelectedListener(item -> { int id = item.getItemId(); // 处理菜单点击逻辑,比如跳转到不同页面 switch (id) { case R.id.nav_home: // 跳转到首页,如果当前已经是首页可以不处理 if (!(this instanceof MainActivity)) { startActivity(new Intent(this, MainActivity.class)); finish(); } break; case R.id.nav_profile: startActivity(new Intent(this, ProfileActivity.class)); finish(); break; // 其他菜单项逻辑... } // 点击后关闭抽屉 drawerLayout.closeDrawer(GravityCompat.START); return true; }); } // 提供给子类的方法:打开抽屉 protected void openDrawer() { drawerLayout.openDrawer(GravityCompat.START); } // 提供给子类的方法:关闭抽屉 protected void closeDrawer() { drawerLayout.closeDrawer(GravityCompat.START); } // 处理ActionBar按钮点击(抽屉开关) @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { if (drawerLayout.isDrawerOpen(GravityCompat.START)) { closeDrawer(); } else { openDrawer(); } return true; } return super.onOptionsItemSelected(item); } // 处理返回键:如果抽屉打开,先关闭抽屉 @Override public void onBackPressed() { if (drawerLayout.isDrawerOpen(GravityCompat.START)) { closeDrawer(); } else { super.onBackPressed(); } } }
2. 编写activity_base.xml布局
这个布局是BaseActivity的基础布局,核心是用DrawerLayout作为根容器,里面包含两部分:
- 一个
FrameLayout:用来承载子类Activity的具体内容 NavigationView:导航抽屉的菜单区域
<?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/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!-- 主内容区域:子类Activity的内容会替换这里 --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 导航抽屉 --> <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" <!-- 抽屉头部布局,按需自定义 --> app:menu="@menu/activity_main_drawer" /> <!-- 抽屉菜单,按需自定义 --> </androidx.drawerlayout.widget.DrawerLayout>
3. 子类Activity继承BaseActivity
现在任何需要导航抽屉的Activity只需要继承BaseActivity,然后把自己的内容加载到content_frame中即可:
比如MainActivity.java:
public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 不要调用setContentView,而是把当前Activity的布局加载到content_frame里 getLayoutInflater().inflate(R.layout.activity_main, findViewById(R.id.content_frame)); // 这里可以写MainActivity自己的业务逻辑 } }
对应的activity_main.xml就是你原本的主页面布局,不需要包含DrawerLayout,直接写页面内容即可。
注意事项
- 菜单动态修改:如果某个子类Activity需要修改抽屉菜单的状态(比如隐藏某个菜单项、修改选中状态),可以在子类的
onCreate中通过navigationView.getMenu()来操作,比如navigationView.getMenu().findItem(R.id.nav_profile).setVisible(false); - 状态保存:如果需要保存抽屉的打开/关闭状态,BaseActivity已经通过
onCreate中的savedInstanceState处理了(因为继承了AppCompatActivity,默认会保存视图状态),如果有额外状态需要保存,可以重写onSaveInstanceState方法 - Theme设置:确保所有继承BaseActivity的Activity都使用带有ActionBar的Theme,比如
Theme.MaterialComponents.Light.DarkActionBar,否则抽屉的开关按钮会显示异常
内容的提问来源于stack exchange,提问作者Eddie
相关产品推荐
相关产品推荐

