如何在Android Google Maps Activity中自定义Toolbar并添加抽屉菜单
嘿,我来帮你搞定这个问题!你遇到的情况其实是AppCompatActivity的默认行为导致的,咱们一步步拆解解决:
一、为什么会自动出现那个默认Toolbar?
当你的Activity继承AppCompatActivity时,系统会默认使用带有ActionBar的主题(比如Theme.MaterialComponents.Light.DarkActionBar这类),哪怕你的布局文件里没写任何Toolbar相关代码,系统也会自动在页面顶部添加一个默认ActionBar,显示你的Activity名称。
二、如何访问并修改这个默认Toolbar?
你不需要在布局里定义任何控件,直接通过getSupportActionBar()就能获取到这个默认的ActionBar实例,然后进行自定义:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityNativeMapBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); // 获取默认ActionBar ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { // 修改标题 actionBar.setTitle("我的地图"); // 显示返回箭头按钮 actionBar.setDisplayHomeAsUpEnabled(true); // 隐藏默认标题(如果需要) // actionBar.setDisplayShowTitleEnabled(false); } // 地图初始化代码... SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); if (mapFragment != null) mapFragment.getMapAsync(this); }
三、替换为自定义Toolbar(更灵活的控制)
如果想要完全自定义Toolbar的样式、位置或添加其他控件,建议替换成自己的Toolbar,步骤如下:
1. 修改主题,禁用默认ActionBar
在AndroidManifest.xml中给你的NativeMap Activity指定不带ActionBar的主题:
<activity android:name=".NativeMap" android:theme="@style/Theme.YourAppName.NoActionBar"/>
或者在res/values/styles.xml中定义这个主题:
<style name="Theme.YourAppName.NoActionBar" parent="Theme.MaterialComponents.Light.DarkActionBar"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style>
2. 修改布局文件,添加自定义Toolbar
把原来的FragmentContainerView放到一个父布局里,加入自定义的MaterialToolbar(推荐用Material组件的Toolbar,兼容性更好):
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 自定义Toolbar --> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:title="我的地图" app:layout_constraintTop_toTopOf="parent"/> <!-- 地图Fragment --> <androidx.fragment.app.FragmentContainerView android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintTop_toBottomOf="@id/toolbar"/> </androidx.constraintlayout.widget.ConstraintLayout>
3. 在Activity中绑定自定义Toolbar
把自定义Toolbar设置为当前Activity的ActionBar,这样就能用getSupportActionBar()继续操作它:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityNativeMapBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); // 将自定义Toolbar设置为ActionBar setSupportActionBar(binding.toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } // 地图初始化代码... }
四、添加抽屉菜单(Navigation Drawer)
要给这个地图页面加抽屉菜单,需要用DrawerLayout包裹整个布局,步骤如下:
1. 修改布局文件,加入DrawerLayout和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"> <!-- 主内容区域(Toolbar + 地图) --> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:title="我的地图" app:layout_constraintTop_toTopOf="parent"/> <androidx.fragment.app.FragmentContainerView android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintTop_toBottomOf="@id/toolbar"/> </androidx.constraintlayout.widget.ConstraintLayout> <!-- 侧边抽屉菜单 --> <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:menu="@menu/drawer_menu"/> <!-- 这里引用菜单文件 --> </androidx.drawerlayout.widget.DrawerLayout>
2. 创建抽屉菜单文件
在res/menu/目录下新建drawer_menu.xml,定义抽屉里的选项:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/nav_home" android:title="首页"/> <item android:id="@+id/nav_settings" android:title="设置"/> <item android:id="@+id/nav_about" android:title="关于"/> </menu>
3. 在Activity中初始化抽屉逻辑
添加抽屉的开关监听和菜单点击事件:
public class NativeMap extends AppCompatActivity implements OnMapReadyCallback { private static final String TAG = NativeMap.class.getSimpleName(); private GoogleMap mMap; private ActivityNativeMapBinding binding; private DrawerLayout drawerLayout; private ActionBarDrawerToggle drawerToggle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityNativeMapBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); // 设置自定义Toolbar setSupportActionBar(binding.toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } // 初始化抽屉开关 drawerLayout = binding.drawerLayout; drawerToggle = new ActionBarDrawerToggle( this, drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawerLayout.addDrawerListener(drawerToggle); drawerToggle.syncState(); // 设置抽屉菜单点击事件 binding.navView.setNavigationItemSelectedListener(item -> { switch (item.getItemId()) { case R.id.nav_home: // 处理首页点击逻辑 break; case R.id.nav_settings: // 处理设置点击逻辑 break; case R.id.nav_about: // 处理关于点击逻辑 break; } // 点击后关闭抽屉 drawerLayout.closeDrawer(GravityCompat.START); return true; }); // 地图初始化代码... SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); if (mapFragment != null) mapFragment.getMapAsync(this); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; LatLng sydney = new LatLng(-34, 151); mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); } // 同步抽屉开关状态 @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); drawerToggle.syncState(); } // 配置变化时更新抽屉开关 @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); drawerToggle.onConfigurationChanged(newConfig); } // 处理抽屉开关按钮点击 @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (drawerToggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); } }
4. 添加抽屉字符串资源
在res/values/strings.xml中添加抽屉的打开/关闭提示文本:
<string name="navigation_drawer_open">打开抽屉</string> <string name="navigation_drawer_close">关闭抽屉</string>
内容的提问来源于stack exchange,提问作者Fat Monk

