56

I currently calculate the screen width like this :

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

Since these 2 functions (getDefaultDisplay() and getMetrics()) are now deprecated, what should we use instead ?

Ryan M
  • 18,333
  • 31
  • 67
  • 74
Greelings
  • 4,964
  • 7
  • 34
  • 70
  • What size, exactly, are you trying to calculate? Should it include status bars, nav bars, cutouts? I've been trying the replacements suggested in the documentation and failing to find any combination of options that produce the same height as this code, which suggests to me that this code probably isn't calculating a particularly useful metric. – Ryan M Aug 14 '20 at 08:39
  • context.getDisplay() is the alternative rather than context.getWindowManager().getDefaultDisplay() – Jashanpreet singh Chakkal Apr 13 '21 at 04:01
  • @JashanChakkal but getMetrics() of getDisplay() is still deprecated. – chitgoks Oct 16 '21 at 12:53

9 Answers9

69

For calculating screen width minus any system bars, this should work:

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

Note that this isn't exactly the same: testing this with the height produces different results, and I've been unable to replicate the old API's functionality with the new API (partly due to the behavior of the old API being a bit tricky to reason about and not always what you want, hence its deprecation). In practice, though, it should be good enough as a generic screen width for many things.

Ryan M
  • 18,333
  • 31
  • 67
  • 74
  • 2
    I was reading the documentation and I also think this is how it should be done. You are right. – Greelings Aug 14 '20 at 09:22
  • your new and deprecated method return different results for pixel 4 in landscape orientation – user924 Dec 10 '20 at 17:43
  • 3
    I guess we should also use `window.decorView.rootWindowInsets?.displayCutout` (`safeInsetLeft` and `safeInsetRight`), those ones aren't all `0`, some > 0 – user924 Dec 10 '20 at 17:52
  • 1
    There is a problem when using windowMetrics.getWindowInsets(), when the screen rotate forcibly from portrait to landscape, Insets.left and insets.right return always 0. – CodingBruceLee Sep 27 '21 at 12:47
  • 3
    and density for R+? – user924 Dec 24 '21 at 10:45
12
@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
        }
    }

This requires androidx.core version 1.5.x

Community
  • 1
  • 1
9

Running into the same problem in 2022, there's a newish Jetpack library for handling this across the various API versions.

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

One caveat I ran into as well is that including androidx.window ended up pulling in tens of thousands of library methods that put me above the DEX 64k method limit, so I had to figure out how optimize those out using the R8/proguard settings, but that's another problem.

dragonx
  • 14,963
  • 27
  • 44
5

I guess I have successfully implement equivalent methods (improving @RyanM's) to deprecated one.

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

The points are

  1. SystemBar's height is not included in Window bound's height. We should exclude NavigationBar's height only (unless it is in landscape mode on phone device).
  2. In landscape mode on phone device, we should exclude NavigationBar's size from Window bound's width.

TESTS

1. On my real phone (API-30)

portrait:

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)

landscape:

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. On emulated Nexus10 (API-31)

portrait:

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)

lanscape:

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. On emulated Nexus7 (API-31)

portrait:

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)

landscape:

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)
hata
  • 11,633
  • 6
  • 46
  • 69
4
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
1

Most of the answers here using windowManager.getCurrentWindowMetrics() method to get the screen size. But this won't give the actual(physical device) screen size. If the screen is in split mode the application will occupy only some portion of the screen and it will give that size instead of full-screen size.

So to get the full-screen size it is recommended to use windowManager.getMaximumWindowMetrics() method.

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
Rect bounds = metrics.getBounds();
int width = bounds.width();
int height = bounds.height();
Chandrakanth
  • 3,711
  • 2
  • 18
  • 31
0

Building upon previous answers; adding the display cutouts:

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;
    }
}
Jose Gómez
  • 3,110
  • 2
  • 32
  • 54
0

This is a DisplayUtils class. You can get the current display Height and width from it. You can get width and height for Dialogs or Dialog fragments where the dialog's width should be 80% of the screen. That is done by the 'getDeviceDisplayWidth' function which accepts the 'reductionPercent' parameter.

object DisplayUtils {

    data class DisplaySize(
        val height: Int,
        val width: Int,
    )

    fun Context.getDeviceDisplaySize(): DisplaySize {
        val height: Int
        val width: Int
        val wm = this.getSystemService(Context.WINDOW_SERVICE) as WindowManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            val metrics = wm.currentWindowMetrics
            val insets = metrics.windowInsets.getInsets(WindowInsets.Type.systemBars())
            height = metrics.bounds.height() - insets.bottom - insets.top
            width = metrics.bounds.width() - insets.left - insets.right

        } else {
            val displayMetrics = DisplayMetrics()

            @Suppress("DEPRECATION")
            val display = wm.defaultDisplay // deprecated in API 30
            @Suppress("DEPRECATION")
            display.getMetrics(displayMetrics) // deprecated in API 30

            height = displayMetrics.heightPixels
            width = displayMetrics.widthPixels
        }
        return DisplaySize(height = height, width = width)
    }

    fun Context.getDeviceDisplayWidth(reductionPercent: Float = 0.16f): Int {
        val size = getDeviceDisplaySize()
        return (size.width - (reductionPercent * size.width)).toInt()
    }

    fun Context.getDeviceDisplayHeight(reductionPercent: Float = 0.16f): Int {
        val size = getDeviceDisplaySize()
        return (size.height - (reductionPercent * size.height)).toInt()
    }

}
Nafis Kabbo
  • 568
  • 5
  • 8
-5

You can use

Context.getDisplay() instead of getDefaultDisplay()

display.getRealMatrix(displayMetrics) instead of display.getMetrics(displayMetrics)

Ravi Makwana
  • 2,782
  • 1
  • 29
  • 41
  • 1
    `display.getRealMatrix` returns full size of your screen (which includes status bar size and navigation bar size) – user924 Dec 10 '20 at 17:20