升级Material1.6.1后BottomNavigationView inflateMenu空指针崩溃
问题现象
- 操作场景:向
BottomNavigationView中动态加载菜单时触发崩溃 - 触发条件:将Material依赖从
com.google.android.material:material:1.2.1升级到com.google.android.material:material:1.6.1后出现,相同代码在1.2.1版本运行无异常 - 崩溃位置:调用
bottomNavigationView.inflateMenu(R.menu.menu_bottom_navigation)时抛出异常 - 核心报错:
java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.ViewGroup.getPaddingBottom()' on a null object reference
复现代码
MainActivity 逻辑
public class MainActivity extends AppCompatActivity { private final BottomNavigationView.OnNavigationItemSelectedListener bottomNavListener = item -> { // 选中逻辑省略 return true; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); bottomNavigationView.setItemIconTintList(null); bottomNavigationView.setOnNavigationItemSelectedListener(bottomNavListener); bottomNavigationView.getMenu().clear(); bottomNavigationView.inflateMenu(R.menu.menu_bottom_navigation); // 崩溃触发行 } }
菜单资源 menu_bottom_navigation.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/bottom_nav_home" android:icon="@drawable/bottom_nav_home_icon_selector" android:title="@string/home_title" /> <item android:id="@+id/bottom_nav_screen2" android:icon="@drawable/bottom_nav_screen2_icon_selector" android:title="@string/savings_screen2" /> <item android:id="@+id/bottom_nav_screen3" android:icon="@drawable/bottom_nav_screen3_icon_selector" android:title="@string/screen3_title" /> <item android:id="@+id/bottom_nav_screen4" android:icon="@drawable/bottom_nav_screen4_icon_selector" android:title="@string/screen4_title" /> <item android:id="@+id/bottom_nav_screen5" android:icon="@drawable/bottom_nav_screen5_icon_selector" android:title="@string/screen5_title" /> </menu>
布局资源 activity_main.xml
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:id="@+id/layout" android:layout_height="match_parent" tools:context=".ui.main.MainActivity"> <androidx.fragment.app.FragmentContainerView android:id="@+id/main_fragment_container" android:layout_width="match_parent" android:layout_height="0dp" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" app:layout_constraintTop_toTopOf="parent" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" app:itemTextAppearanceActive="@style/BottomNavigationView.Active" app:itemTextAppearanceInactive="@style/BottomNavigationView" app:itemTextColor="@drawable/bottom_nav_selector" app:labelVisibilityMode="labeled" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/main_fragment_container" /> </androidx.constraintlayout.widget.ConstraintLayout>
完整崩溃日志
2022-06-14 19:49:18.562 18439-18439/com.example.android.debug E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.android.debug, PID: 18439 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.debug/com.example.android.ui.main.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.view.ViewGroup.getPaddingBottom()' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
问题根因
从Material库1.4.0版本开始,BottomNavigationView调整了内部菜单容器的初始化逻辑:调用getMenu().clear()清空菜单时,内部承载菜单项的导航条容器会被临时移除回收,后续如果同步调用inflateMenu(),新版本逻辑会直接访问还未重新创建的导航条容器,调用其getPaddingBottom()方法时触发空指针。
1.2.1版本不存在这个问题是因为旧版本clear操作不会销毁内部容器,inflate时可以正常访问。
修复方案
两种方案选其一即可:
- 方案一(推荐):删除多余的
bottomNavigationView.getMenu().clear()调用。inflateMenu()方法本身在加载新菜单前就会自动清空原有菜单,不需要手动提前清空,修改后的代码兼容所有版本的Material库。 - 方案二:如果业务逻辑确实需要先手动清空菜单,将inflate操作post到视图消息队列,等内部状态更新完成后再执行加载:
bottomNavigationView.getMenu().clear(); bottomNavigationView.post(() -> { bottomNavigationView.inflateMenu(R.menu.menu_bottom_navigation); });
补充提示:当前菜单配置了5个item,升级到更高版本Material库如果出现文字位移、选中项放大异常,可以在布局中给
BottomNavigationView添加app:itemHorizontalTranslationEnabled="false"属性,关闭水平位移动画,保持所有item样式统一。
内容的提问来源于stack exchange,提问作者Sumit Jain
相关产品推荐
相关产品推荐

