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

2eafrhcq  于 2023-06-20  发布在  Android
关注(0)|答案(8)|浏览(403)

我现在是这样计算屏幕宽度的:

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

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

zd287kbt

zd287kbt1#

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

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;
    }
}

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

cczfrluj

cczfrluj2#

@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

wpx232ag

wpx232ag3#

在2022年遇到同样的问题,有一个新的Jetpack库可以在各种API版本中处理这个问题。
build.gradle

dependencies {
    implementation 'androidx.window:window:1.0.0'

}
import androidx.window.layout.WindowMetrics;
import androidx.window.layout.WindowMetricsCalculator;

WindowMetrics windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity);
final int height = windowMetrics.getBounds().height();
final int width = windowMetrics.getBounds().width();

我遇到的一个警告是,包括androidx.window最终会拉入数万个库方法,使我超过DEX 64 k方法限制,所以我必须弄清楚如何使用R8/proguard设置优化这些方法,但这是另一个问题。

vbopmzt1

vbopmzt14#

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

@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的高度。我们应该只排除NavigationBar的高度(除非它在手机设备上处于横向模式)。
1.在手机设备的横向模式下,我们应该从窗口边界的宽度中排除NavigationBar的大小。

测试

    • 1.在我的真实手机上(API-30)**

肖像:

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

景观:

2021-12-14 22:17:35.858 31660-31660/com.stackoverflow.windowmetrics D/Upto API-29: (width, height) = (2016, 1080)
2021-12-14 22:17:35.887 31660-31660/com.stackoverflow.windowmetrics D/API-30+: (width, height) = (2016, 1080)
    • 2.在模拟Nexus10(API-31)上**

肖像:

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

景观:

2021-12-14 22:18:44.809 1416-1416/com.stackoverflow.windowmetrics D/Upto API-29: (width, height) = (2560, 1504)
2021-12-14 22:18:44.814 1416-1416/com.stackoverflow.windowmetrics D/API-30+: (width, height) = (2560, 1504)
    • 2.在模拟Nexus7(API-31)上**

肖像:

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

景观:

2021-12-14 22:22:23.283 3108-3108/com.stackoverflow.windowmetrics D/Upto API-29: (width, height) = (1280, 736)
2021-12-14 22:22:23.289 3108-3108/com.stackoverflow.windowmetrics D/API-30+: (width, height) = (1280, 736)
6jjcrrmo

6jjcrrmo5#

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
busg9geu

busg9geu6#

这里大部分的答案都是用windowManager.getCurrentWindowMetrics()方法来得到屏幕大小。但这不会给予实际的(物理设备)屏幕大小。如果屏幕处于拆分模式,应用程序将只占用屏幕的一部分,并且它将给予该大小而不是全屏大小。
因此,要获得全屏大小,建议使用windowManager.getMaximumWindowMetrics()方法。

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
Rect bounds = metrics.getBounds();
int width = bounds.width();
int height = bounds.height();
suzh9iv8

suzh9iv87#

基于以前的答案;添加显示剪切:

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();

        if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE
                && activity.getResources().getConfiguration().smallestScreenWidthDp < 600) {
                // landscape and phone
                Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
                Insets cutout = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(WindowInsets.Type.displayCutout());
                return bounds.width() - insets.right - insets.left - cutout.right - cutout.left;
        } else {
            // portrait or tablet
            return bounds.width();
        }
    } else {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }
}
68de4m5k

68de4m5k8#

可以使用

Context.getDisplay()而不是getDefaultDisplay()
display.getRealMatrix(displayMetrics)而不是display.getMetrics(displayMetrics)

相关问题