You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 06:52:51