Obtención del ancho de pantalla en el nivel 30 de API (Android 11): getDefaultDisplay() y getMetrics() ahora están en desuso. ¿Qué deberíamos usar en su lugar?

6 minutos de lectura

Avatar de usuario de Denis
Denis

Actualmente calculo el ancho de la pantalla así:

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

Dado que estas 2 funciones (getDefaultDisplay() y getMetrics()) ahora están en desuso, ¿qué deberíamos usar en su lugar?

  • ¿Qué tamaño, exactamente, estás tratando de calcular? ¿Debería incluir barras de estado, barras de navegación, recortes? He estado probando los reemplazos sugeridos en la documentación y no he podido encontrar ninguna combinación de opciones que produzca la misma altura que este código, lo que me sugiere que este código probablemente no esté calculando una métrica particularmente útil.

    – Ryan M.

    14 de agosto de 2020 a las 8:39


  • context.getDisplay() es la alternativa en lugar de context.getWindowManager().getDefaultDisplay()

    – Jashan Chakkal

    13 de abril de 2021 a las 4:01

  • @JashanChakkal pero getMetrics() de getDisplay() todavía está en desuso.

    – Chitgoks

    16 oct 2021 a las 12:53

Para calcular el ancho de la pantalla menos las barras del sistema, esto debería funcionar:

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

Tenga en cuenta que esto no es exactamente lo mismo: probar esto con la altura produce resultados diferentes, y no he podido replicar la funcionalidad de la API anterior con la API nueva (en parte debido a que el comportamiento de la API anterior es un poco complicado de razonar y no siempre lo que usted quiere, de ahí su desaprobación). Sin embargo, en la práctica, debería ser lo suficientemente bueno como un ancho de pantalla genérico para muchas cosas.

  • Estaba leyendo la documentación y también creo que así es como debería hacerse. Tienes razón.

    – Denis

    14 de agosto de 2020 a las 9:22

  • su método nuevo y obsoleto arroja resultados diferentes para el píxel 4 en orientación horizontal

    – usuario924

    10 de diciembre de 2020 a las 17:43


  • Supongo que también deberíamos usar window.decorView.rootWindowInsets?.displayCutout (safeInsetLeft y safeInsetRight), esos no son todos 0algunos > 0

    – usuario924

    10 de diciembre de 2020 a las 17:52


  • Hay un problema cuando se usa windowMetrics.getWindowInsets(), cuando la pantalla gira a la fuerza de vertical a horizontal, Insets.left e insets.right devuelven siempre 0.

    – CodificaciónBruceLee

    27 de septiembre de 2021 a las 12:47

  • y densidad para R+?

    – usuario924

    24 de diciembre de 2021 a las 10:45

Avatar de usuario de Vincent Joshua Tigas
Vicente Josué Tigas

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

Esto requiere androidx.core versión 1.5.x

  • Su RequiresApi está mal, requiere API nivel 23.

    – Xam

    25 de agosto de 2021 a las 21:22

Al encontrarse con el mismo problema en 2022, hay una biblioteca Jetpack más nueva para manejar esto en las diversas versiones de API.

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

Una advertencia con la que me encontré también es que incluir androidx.window terminé obteniendo decenas de miles de métodos de biblioteca que me colocaron por encima del límite de método DEX 64k, así que tuve que descubrir cómo optimizarlos usando la configuración de R8/proguard, pero ese es otro problema.

avatar de usuario de hata
hata

Supongo que he implementado con éxito métodos equivalentes (mejorando los de @RyanM) para uno obsoleto.

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

los puntos son

  1. La altura de SystemBar no está incluida en la altura del límite de la ventana. Deberíamos excluir solo la altura de la barra de navegación (a menos que esté en modo horizontal en el dispositivo del teléfono).
  2. En modo horizontal en el dispositivo del teléfono, debemos excluir el tamaño de la barra de navegación del ancho del límite de la ventana.

PRUEBAS

1. En mi teléfono real (API-30)

retrato:

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)

paisaje:

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. En Nexus10 emulado (API-31)

retrato:

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)

paisaje:

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. En Nexus7 emulado (API-31)

retrato:

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)

paisaje:

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)

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

Avatar de usuario de Ravi Makwana
Ravi Makwana

Puedes usar

Context.getDisplay() en vez de getDefaultDisplay()

display.getRealMatrix(displayMetrics) en vez de display.getMetrics(displayMetrics)

¿Ha sido útil esta solución?