在 API 级别 30 (Android 11) 上获取屏幕宽度:getDefaultDisplay() 和 getMetrics() 现在已弃用。我们应该用什么代替?

Getting screen width on API Level 30 (Android 11): getDefaultDisplay() and getMetrics() are now deprecated. What should we use instead?

我目前是这样计算屏幕宽度的:

public static int getScreenWidth(@NonNull Context context) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    return displayMetrics.widthPixels;
}

由于这 2 个函数(getDefaultDisplay()getMetrics())现已弃用,我们应该使用什么来代替?

要计算屏幕宽度减去任何系统栏,这应该有效:

public static int getScreenWidth(@NonNull Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
        Insets insets = windowMetrics.getWindowInsets()
                .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
        return windowMetrics.getBounds().width() - insets.left - insets.right;
    } else {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }
}

请注意,这完全不一样:用高度测试它会产生不同的结果,我无法复制旧的API' s 与新 API 的功能(部分原因是旧 API 的行为有点难以推理,而且并不总是你想要的,因此它被弃用了)。但在实践中,对于很多事情来说,它作为通用屏幕宽度应该足够好。

您可以使用

Context.getDisplay() 而不是 getDefaultDisplay()

display.getRealMatrix(displayMetrics) 而不是 display.getMetrics(displayMetrics)

@RequiresApi(20)
inline val Fragment.windowHeight: Int
    get() {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val metrics = requireActivity().windowManager.currentWindowMetrics
            val insets = metrics.windowInsets.getInsets(WindowInsets.Type.systemBars())
            metrics.bounds.height() - insets.bottom - insets.top
        } else {
            val view = requireActivity().window.decorView
            val insets = WindowInsetsCompat.toWindowInsetsCompat(view.rootWindowInsets, view).getInsets(WindowInsetsCompat.Type.systemBars())
            resources.displayMetrics.heightPixels - insets.bottom - insets.top
        }
    }

@RequiresApi(20)
inline val Fragment.windowWidth: Int
    get() {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val metrics = requireActivity().windowManager.currentWindowMetrics
            val insets = metrics.windowInsets.getInsets(WindowInsets.Type.systemBars())
            metrics.bounds.width() - insets.left - insets.right
        } else {
            val view = requireActivity().window.decorView
            val insets = WindowInsetsCompat.toWindowInsetsCompat(view.rootWindowInsets, view).getInsets(WindowInsetsCompat.Type.systemBars())
            resources.displayMetrics.widthPixels - insets.left - insets.right
        }
    }

这需要 androidx.core 版本 1.5.x

我想我已经成功地实现了与弃用方法等效的方法(改进 )。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Deprecated older method for comparison.
    DisplayMetrics outMetrics = new DisplayMetrics();
    getDisplay().getMetrics(outMetrics);
    Log.d("Upto API-29", String.format(
            "(width, height) = (%d, %d)", outMetrics.widthPixels, outMetrics.heightPixels
    ));

    // Newer methods.
    Log.d("API-30+", String.format(
            "(width, height) = (%d, %d)", getScreenWidth(this), getScreenHeight(this)
    ));
}

public static int getScreenWidth(@NonNull Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
        Rect bounds = windowMetrics.getBounds();
        Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
                WindowInsets.Type.systemBars()
        );

        if (activity.getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE
                && activity.getResources().getConfiguration().smallestScreenWidthDp < 600
        ) { // landscape and phone
            int navigationBarSize = insets.right + insets.left;
            return bounds.width() - navigationBarSize;
        } else { // portrait or tablet
            return bounds.width();
        }
    } else {
        DisplayMetrics outMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
        return outMetrics.widthPixels;
    }
}

public static int getScreenHeight(@NonNull Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();
        Rect bounds = windowMetrics.getBounds();
        Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
                WindowInsets.Type.systemBars()
        );

        if (activity.getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE
                && activity.getResources().getConfiguration().smallestScreenWidthDp < 600
        ) { // landscape and phone
            return bounds.height();
        } else { // portrait or tablet
            int navigationBarSize = insets.bottom;
            return bounds.height() - navigationBarSize;
        }
    } else {
        DisplayMetrics outMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
        return outMetrics.heightPixels;
    }
}

积分是

  1. SystemBar 的高度不包含在Window 边界的高度中。我们应该只排除 NavigationBar 的高度(除非它在 ​​phone 设备上处于横向模式)。
  2. 在 phone 设备的横向模式下,我们应该从 Window 边界的宽度中排除 NavigationBar 的大小。

测试

1.在我真实的 phone (API-30)

纵向:

2021-12-14 22:17:28.231 31660-31660/com.Whosebug.windowmetrics D/Upto API-29: (width, height) = (1080, 2016)
2021-12-14 22:17:28.237 31660-31660/com.Whosebug.windowmetrics D/API-30+: (width, height) = (1080, 2016)

横向:

2021-12-14 22:17:35.858 31660-31660/com.Whosebug.windowmetrics D/Upto API-29: (width, height) = (2016, 1080)
2021-12-14 22:17:35.887 31660-31660/com.Whosebug.windowmetrics D/API-30+: (width, height) = (2016, 1080)

2。在模拟 Nexus10 (API-31)

纵向:

2021-12-14 22:19:33.379 1416-1416/com.Whosebug.windowmetrics D/Upto API-29: (width, height) = (1600, 2464)
2021-12-14 22:19:33.382 1416-1416/com.Whosebug.windowmetrics D/API-30+: (width, height) = (1600, 2464)

横向:

2021-12-14 22:18:44.809 1416-1416/com.Whosebug.windowmetrics D/Upto API-29: (width, height) = (2560, 1504)
2021-12-14 22:18:44.814 1416-1416/com.Whosebug.windowmetrics D/API-30+: (width, height) = (2560, 1504)

2。在模拟 Nexus7 (API-31)

纵向:

2021-12-14 22:21:21.606 3108-3108/com.Whosebug.windowmetrics D/Upto API-29: (width, height) = (800, 1216)
2021-12-14 22:21:21.610 3108-3108/com.Whosebug.windowmetrics D/API-30+: (width, height) = (800, 1216)

横向:

2021-12-14 22:22:23.283 3108-3108/com.Whosebug.windowmetrics D/Upto API-29: (width, height) = (1280, 736)
2021-12-14 22:22:23.289 3108-3108/com.Whosebug.windowmetrics D/API-30+: (width, height) = (1280, 736)
val windowMetrics = requireActivity().windowManager.currentWindowMetrics
val displayMetrics = resources.displayMetrics

val pxHeight = windowMetrics.bounds.height()
val pxWidth  = windowMetrics.bounds.width()

val density  = displayMetrics.density

val dpHeight = pxHeight/density
val dpWidth  = pxWidth/density