如何在onResume()之前/期间获取Activity内容视图尺寸
解决在onResume()中提前获取无系统栏的最大内容尺寸问题
哈哈,我太懂这种抓耳挠腮的感觉了——明明觉得应该有个简单办法,结果试了常规操作都卡壳!别慌,我来给你捋几个靠谱的方案,完美解决你在onResume()里提前拿内容尺寸的需求:
方案一:用WindowInsetsCompat(推荐,适配Android 11+及旧版本)
Android 11之后官方推荐用WindowInsets来获取系统栏的尺寸,配合Jetpack的WindowInsetsCompat可以兼容更多版本。核心思路是监听DecorView的布局完成事件,拿到系统栏的Insets后,计算出可用的内容区域:
override fun onResume() { super.onResume() val decorView = window.decorView // 监听布局完成,确保能拿到准确的Insets和视图尺寸 decorView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { // 先移除监听,避免重复触发 decorView.viewTreeObserver.removeOnGlobalLayoutListener(this) // 转换为兼容版WindowInsets val insets = WindowInsetsCompat.toWindowInsetsCompat(decorView.rootWindowInsets) // 获取系统栏(状态栏+导航栏)的尺寸 val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()) // 计算可用的宽高:DecorView的测量尺寸减去系统栏的边距 val usableWidth = decorView.measuredWidth - systemBarsInsets.left - systemBarsInsets.right val usableHeight = decorView.measuredHeight - systemBarsInsets.top - systemBarsInsets.bottom // 这里拿到的就是不含状态栏和导航栏的最大内容尺寸啦 Log.d("ContentSize", "可用宽度: $usableWidth, 可用高度: $usableHeight") } }) }
方案二:兼容旧版本Android(API < 20)
如果你的应用需要兼容Android 4.4以下的版本,可以通过资源ID获取系统栏高度,再结合屏幕尺寸计算:
@Override protected void onResume() { super.onResume(); final View decorView = getWindow().getDecorView(); decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 移除监听,适配不同API版本 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { decorView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } // 获取状态栏高度 int statusBarHeight = 0; int statusBarResId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (statusBarResId > 0) { statusBarHeight = getResources().getDimensionPixelSize(statusBarResId); } // 获取导航栏高度(先判断设备是否有导航栏) int navBarHeight = 0; int navBarResId = getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (navBarResId > 0 && hasNavigationBar()) { navBarHeight = getResources().getDimensionPixelSize(navBarResId); } // 获取屏幕总尺寸 DisplayMetrics displayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; // 计算可用尺寸 int usableWidth = screenWidth; int usableHeight = screenHeight - statusBarHeight - navBarHeight; Log.d("ContentSize", "可用宽度: " + usableWidth + ", 可用高度: " + usableHeight); } }); } // 判断设备是否存在导航栏的辅助方法 private boolean hasNavigationBar() { boolean hasNav = false; Resources res = getResources(); int configResId = res.getIdentifier("config_showNavigationBar", "bool", "android"); if (configResId > 0) { hasNav = res.getBoolean(configResId); } // 处理模拟器的特殊情况 try { Class<?> systemPropsClass = Class.forName("android.os.SystemProperties"); Method getMethod = systemPropsClass.getMethod("get", String.class); String navOverride = (String) getMethod.invoke(null, "qemu.hw.mainkeys"); if ("1".equals(navOverride)) { hasNav = false; } else if ("0".equals(navOverride)) { hasNav = true; } } catch (Exception e) { // 忽略异常,用默认判断逻辑 } return hasNav; }
为什么之前的方法不行?
DisplayMetrics返回的是整个屏幕的尺寸,包含状态栏和导航栏,所以不符合你的需求;- Activity的内容视图在
onResume()结束前还没完成布局,直接拿它的尺寸会得到0或者不准确的值; - 我们用
ViewTreeObserver.OnGlobalLayoutListener的原因是,这个回调会在视图第一次完成布局后立即触发,刚好在内容显示之前,完美契合你的时间点要求。
内容的提问来源于stack exchange,提问作者Rajveer
相关产品推荐
相关产品推荐

