Android NavigationView导航抽屉Bug:部分未选中项文本莫名加粗
问题描述
使用Android NavigationView实现导航抽屉时,当菜单包含15个以上选项,选中某一项后,随机有1-2个未选中项的文本变为粗体。
相关代码
NavigationView布局代码
<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:background="@color/colorMenuBg" android:fitsSystemWindows="true" android:maxWidth="268dp" app:headerLayout="@layout/nav_header" app:menu="@menu/nav_menu" app:itemHorizontalPadding="28dp" app:itemIconPadding="18dp" app:itemIconSize="24dp" app:itemIconTint="@drawable/drawer_item" app:itemTextColor="@drawable/drawer_item">
菜单项示例代码
<item android:id="@+id/nav_videos" android:icon="@drawable/cat_icon_videos" android:checkable="true" android:title="@string/all_videos"/> <item android:id="@+id/nav_pics" android:icon="@drawable/cat_icon_gallery" android:checkable="true" android:title="@string/all_pics"/> <item android:id="@+id/nav_gifs" android:icon="@drawable/cat_icon_gif" android:checkable="true" android:title="@string/all_gifs"/>
当前onNavigationItemSelected代码片段
override fun onNavigationItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.nav_latest -> { if (currentGal != "latest") { getMemes("latest") } } R.id.nav_top -> { if (currentGal != "top") { getMemes("top") } } R.id.nav_random -> { if (currentGal != "random") { getMemes("random") } }
解决方案
这个问题是NavigationView内部RecyclerView的View Holder复用导致的视图状态错乱,菜单选项过多时触发复用,未正确重置非选中项的文本样式。以下是几种可行修复方案:
1. 启用菜单单选模式
在nav_menu.xml的根<menu>标签中添加单选行为配置,让系统自动管理选中状态:
<menu xmlns:android="http://schemas.android.com/apk/res/android" android:checkableBehavior="single"> <!-- 所有菜单项 --> </menu>
2. 在onNavigationItemSelected中手动重置所有项样式
通过遍历菜单项,强制设置选中/未选中状态的文本样式,解决复用导致的错乱:
override fun onNavigationItemSelected(item: MenuItem): Boolean { // 遍历所有菜单项,重置选中状态与文本样式 for (i in 0 until nav_view.menu.size()) { val menuItem = nav_view.menu.getItem(i) val isSelected = menuItem.itemId == item.itemId menuItem.isChecked = isSelected // 设置文本样式:选中为粗体,未选中为正常 menuItem.title = SpannableString(menuItem.title).apply { setSpan( StyleSpan(if (isSelected) Typeface.BOLD else Typeface.NORMAL), 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE ) } } // 原有业务逻辑保留 when (item.itemId) { R.id.nav_latest -> { if (currentGal != "latest") { getMemes("latest") } } R.id.nav_top -> { if (currentGal != "top") { getMemes("top") } } R.id.nav_random -> { if (currentGal != "random") { getMemes("random") } } // 其他菜单项处理 } // 关闭导航抽屉 findViewById<DrawerLayout>(R.id.drawer_layout).closeDrawer(GravityCompat.START) return true }
3. 自定义MenuItem文本样式
通过样式文件明确区分选中与未选中的文本样式,避免复用冲突:
在styles.xml中定义样式:
<style name="NavItemTextNormal"> <item name="android:textSize">14sp</item> <item name="android:textStyle">normal</item> </style> <style name="NavItemTextSelected"> <item name="android:textStyle">bold</item> </style>
在NavigationView布局中引用:
<com.google.android.material.navigation.NavigationView ... app:itemTextAppearance="@style/NavItemTextNormal" app:itemTextAppearanceSelected="@style/NavItemTextSelected" ...>
关于onNavigationItemSelected修复的可行性
可以,通过修改onNavigationItemSelected方法能直接解决问题,核心是手动重置所有菜单项的状态与样式,覆盖View复用带来的异常。上面的方案2就是基于该方法的具体实现。
内容的提问来源于stack exchange,提问作者Eduard Unruh
相关产品推荐
相关产品推荐

