调用findViewById获取状态栏/导航栏背景视图返回null求助
我之前做共享元素转场时也碰到过一模一样的坑,尤其是在Nougat和Oreo这两个版本上,这两个系统对状态栏、导航栏背景视图的创建逻辑确实有点特殊。给你几个亲测有效的尝试方向:
等视图完全加载后再获取:这两个背景视图是依附在DecorView上的,初始化时机比Activity的
onCreate晚很多。如果在onCreate里直接调用findViewById,大概率视图还没被系统创建出来。你可以试试在布局全局加载完成后再获取,用ViewTreeObserver监听:getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 在这里尝试获取目标视图 View statusBarBg = findViewById(android.R.id.statusBarBackground); View navBarBg = findViewById(android.R.id.navigationBarBackground); // 记得移除监听,避免重复触发 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { getWindow().getDecorView().getViewTreeObserver().removeGlobalOnLayoutListener(this); } } });检查主题的半透明配置:如果你的主题里设置了
android:windowTranslucentStatus或android:windowTranslucentNavigation为true,系统会跳过默认背景视图的创建,转而让你的布局内容延伸到状态栏/导航栏区域。这种情况下你要么暂时移除这些属性,要么手动在布局中添加一个模拟背景的视图来用于共享转场。曲线救国:手动模拟背景视图:如果实在拿不到系统自带的背景视图,不妨自己创建一个临时视图来替代,只要尺寸和颜色和系统状态栏/导航栏一致就行。可以通过
WindowInsets获取系统栏的尺寸:View decorView = getWindow().getDecorView(); decorView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { @Override public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { int statusBarHeight = insets.getSystemWindowInsetTop(); int navBarHeight = insets.getSystemWindowInsetBottom(); // 创建模拟状态栏背景的视图 View fakeStatusBar = new View(MainActivity.this); fakeStatusBar.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight)); fakeStatusBar.setBackgroundColor(getResources().getColor(android.R.color.darker_gray)); // 替换成你主题里的状态栏颜色 ((ViewGroup) decorView).addView(fakeStatusBar); // 导航栏同理 View fakeNavBar = new View(MainActivity.this); fakeNavBar.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, navBarHeight)); fakeNavBar.setBackgroundColor(getResources().getColor(android.R.color.darker_gray)); ((ViewGroup) decorView).addView(fakeNavBar); return insets.consumeSystemWindowInsets(); } });排查系统UI可见性设置:虽然你说状态栏和导航栏是可见的,但还是要检查下有没有调用过
setSystemUiVisibility设置过类似SYSTEM_UI_FLAG_FULLSCREEN、SYSTEM_UI_FLAG_HIDE_NAVIGATION这类flag,某些组合可能会间接导致系统不创建背景视图。
另外关于Alex Lockwood评论区的讨论,我记得有开发者提到Oreo上这两个视图的创建时机比Nougat更晚,必须等布局完全稳定后才能拿到,所以OnGlobalLayoutListener的方法应该能解决大部分场景。
内容的提问来源于stack exchange,提问作者ColonD

