18

How can I get the available height of the screen in Android? I need to the height minus the status bar / menu bar or any other decorations that might be on screen and I need it to work for all devices. Also, I need to know this in the onCreate function. I know this question has been asked before but I have already tried their solutions and none of them work. Here are some of the things I have tried:

I have tested this code on API 7 - 17. Unfortunately, on API 13 there is extra space at bottom both horizontally and vertically and on API 10, 8, and 7 there is not enough space at the bottom both horizontally and vertically. (I have not tested on obsolete APIs):

Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;

TypedValue tv = new TypedValue();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
    if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
        screenHeight -= TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}

int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
    screenHeight -= getResources().getDimensionPixelSize(resourceId);

This does not take into account the status bar / menu bar:

Display display = getWindowManager().getDefaultDisplay();
screenWidth = display.getWidth();
screenHeight = display.getHeight();

Neither does this:

Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
screenWidth = size.x;
screenHeight = size.y;

Nor this:

Point size = new Point();
getWindowManager().getDefaultDisplay().getRealSize(size);
screenWidth = size.x;
screenHeight = size.y;

This does not work:

Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
// since SDK_INT = 1;
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
try
{
    // used when 17 > SDK_INT >= 14; includes window decorations (statusbar bar/menu bar)
    screenWidth = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
    screenHeight = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
}
catch (Exception ignored)
{
    // Do nothing
}
try
{
    // used when SDK_INT >= 17; includes window decorations (statusbar bar/menu bar)
    Point realSize = new Point();
    Display.class.getMethod("getRealSize", Point.class).invoke(display, realSize);
    screenWidth = realSize.x;
    screenHeight = realSize.y;
}
catch (Exception ignored)
{
    // Do nothing
}

I then used the following code to subtract the height of the status bar and menu bar from the screen height:

int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
    result = getResources().getDimensionPixelSize(resourceId);
screenHeight -= result;
result = 0;
if (screenHeight >= screenWidth)
    resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
else
    resourceId = getResources().getIdentifier("navigation_bar_height_landscape", "dimen", "android");
if (resourceId > 0)
    result = getResources().getDimensionPixelSize(resourceId);
screenHeight -= result;

On API 17 it correctly calculates the height of the status bar and menu bar in portrait but not in landscape. On API 10, it returns 0. I need it to work ideally on all devices or minimum API 7.

halfer
  • 19,824
  • 17
  • 99
  • 186
Dan Bray
  • 7,242
  • 3
  • 52
  • 70
  • If you really need exact size in onCreate method, you are doing sth wrong. Using global layout listener and runnig you actions when layout phase is done is the right way to do that, like @Carnal answer. In on global layou callback you should use getMessureadHeight rather thant getHeight on the View. – Mikooos Jul 04 '13 at 20:41
  • 1
    @DanBray - Please check out the solution I've put up. It definitely works. – Karthik Balakrishnan Jul 05 '13 at 07:46
  • Any method that defers getting the dimensions using a `OnGlobalLayoutListener` or by posting a `Runnable` to the root view's message queue should work. – MH. Jul 06 '13 at 01:17
  • I would get the decor view and make all the calculations based on the DecorView – Olsi Saqe Mar 16 '16 at 19:57

8 Answers8

7

If you want the the display dimensions in pixels you can use getSize:

Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;

If you're not in an Activity you can get the default Display via WINDOW_SERVICE:

WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
display.getSize(size);
int width = size.x;
int height = size.y;

Before getSize was introduced (in API level 13), you could use the getWidth and getHeight methods that are now deprecated:

Display display = getWindowManager().getDefaultDisplay(); 
int width = display.getWidth();  // deprecated
int height = display.getHeight();  // deprecated

Referred from : Get screen dimensions in pixels

Community
  • 1
  • 1
Ponmalar
  • 6,871
  • 10
  • 50
  • 80
6

Working solution:

Create two dummy views :

 <View
    android:id="@+id/top"
    android:layout_height="0dp"
    android:layout_width="match_parent"
    android:layout_alignParentTop="true" />

<View
        android:id="@+id/bottom"
        android:layout_height="0dp"
        android:layout_width="match_parent"
        android:layout_alignParentBottom="true" />

Now, you should call getLocationOnScreen(int []) on these two views to receive their locations. This function returns an array of 2 integers. The first being x and the second y. We require only y. Also note that correct values are returned only AFTER all the views are created and the onCreate method is exited.

I tested this code on a regular fragment called from a viewpager.

The code was in the onCreateView method of my fragment. I still had to use a postDelayed to get the correct values.

final View top = (View) view.findViewById(R.id.top);
    final View bottom = (View) view.findViewById(R.id.bottom);
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {

            int topLoc[] = new int[2];
            top.getLocationOnScreen(topLoc);
            int BottomLoc[] = new int[2];
            bottom.getLocationOnScreen(BottomLoc);
            Log.d(Constants.debug, "topY: "+ topLoc[1]+" BottomY:" + BottomLoc[1]);
        }
    },4000);

I have a Nexus 4.

In regular mode (status bar and the soft buttons shown) :

07-05 02:28:23.367      565-565/com.torcellite.xx D/xx: topY: 50 BottomY:1182

In full screen mode (no status bar or soft buttons shown) :

07-05 02:29:17.360      707-707/com.torcellite.xx D/xx: topY: 0 BottomY:1280

All you have to do now is subtract.

halfer
  • 19,824
  • 17
  • 99
  • 186
Karthik Balakrishnan
  • 4,353
  • 6
  • 37
  • 69
  • This looks like the most promising solution, however I could not make it work. I think I've done something wrong. I created a XML file called screen.xml and this contains the following code: – Dan Bray Jul 06 '13 at 02:40
  • I then moved my code from onCreate into public `void run() {` I then made all my local variables, global to avoid errors and used this line `context = this;` to get rid of errors for using `this` keyword inside the callback – Dan Bray Jul 06 '13 at 02:43
  • 1
    Could you post the logcat? And try putting my code in `onResume` if its an Activity. – Karthik Balakrishnan Jul 06 '13 at 03:33
  • 1
    I think I know why it crashes. The two views top and bottom have to be in the same XML file that you're setting the content view to in your activity. So if its `main_activity.xml` the views go into that XML file. Not a new one. – Karthik Balakrishnan Jul 06 '13 at 03:45
  • I did put both the views into the same XML file. I had to create a new XML file because I am not using XML files for my program. I don't even even have a `main_activity.xml`. – Dan Bray Jul 06 '13 at 03:56
  • 1
    Okay, then you're not setting a content view? You should inflate these views dynamically in your code then. – Karthik Balakrishnan Jul 06 '13 at 03:56
  • I am setting a context view. The first 3 lines inside my `OnCreate` are `super.onCreate(savedInstanceState); layout = new RelativeLayout(this); setContentView(layout);` – Dan Bray Jul 06 '13 at 04:00
  • 1
    You need to add the views to `layout`. – Karthik Balakrishnan Jul 06 '13 at 04:01
  • 1
    let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32985/discussion-between-torcellite-and-dan-bray) – Karthik Balakrishnan Jul 06 '13 at 04:01
  • I have now added these lines of code `layout.addView(top); layout.addView(bottom);` immediately below `final View top = (View) view.findViewById(R.id.top); final View bottom = (View) view.findViewById(R.id.bottom);`. Unfortunately, my program still won't run – Dan Bray Jul 06 '13 at 04:11
  • @AndroidDev 50ms works too. I just used 4 seconds for development. – Karthik Balakrishnan Jul 31 '13 at 10:31
  • Guys -- why not use display.getSize(size); as in the answer Ponmalar ? – Fattie Jun 03 '14 at 14:53
2

For Display

DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int width = displaymetrics.widthPixels;

For Status Bar

public int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }
Parag Chauhan
  • 35,760
  • 13
  • 86
  • 95
2

I haven't tried different API's but I'm using these values to figure out whether or not the onscreen keyboard is up. Maybe you can try this and something comparable for width to get what you need?

Rect r = new Rect();
view.getWindowVisibleDisplayFrame(r);
int
    screenHeight = view.getRootView().getHeight(), // height of the entire screen
    appHeight = r.bottom - r.top, // height of the entire app
    statusBarHeight = r.top, // height of the statusbar
    pageHeight = view.getHeight(), // height of the app without title
    appTitleHeight = appHeight - pageHeight, // height of the only the app title
    topHeight = statusBarHeight + appTitleHeight;
Gelunox
  • 772
  • 1
  • 5
  • 23
2
final View view = findViewById(R.id.root);
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // calculate the height in here...

        ViewTreeObserver vto = view.getViewTreeObserver();
        vto.removeGlobalOnLayoutListener(this);
    }
});

Use this listener inside onCreate (available since api 1). You will need to assign the id @+id/root to the parent in your xml layout. There is no way the size can return a result of 0, since this listener makes a callback whenever the layouts has been positioned in view.

Carnal
  • 21,744
  • 6
  • 60
  • 75
  • This might work if I was using XML files. I am doing my layout without XML files. Actually, I have 0 XML files for my entire program because I would rather it work dynamically. – Dan Bray Jul 06 '13 at 02:47
  • I got the view using `final View view = getWindow().getDecorView().getRootView();` instead of `final View view = findViewById(R.id.root);` Unfortunately, I am still unable to get the screen height minus any decor. `screenHeight = view.getMeasuredHeight();` or `screenHeight = view.getHeight();` do not give the height without the status bar or menu bar. – Dan Bray Jul 06 '13 at 03:37
  • This will not work in onCreate. This is only fired AFTER the layout has been expanded which occurs AFTER onCreate has executed. – Johann Jul 31 '13 at 09:51
  • 2
    You should have a reading about OnGlobalLayoutListener before writing things you don't understand and downvoting me! – Carnal Aug 05 '13 at 13:47
0

How about setting up a subclassed View as the root of the Activity. Make this View fill the screen (it won't go past the status bar) and override onSizeChanged(). Store those values and write a getter for them.

Ryan
  • 3,579
  • 9
  • 47
  • 59
0

It may not answer your question, but it could be useful to know (I was looking for it myself when I came to this question) that if you need a View's dimension but your code is being executed when its layout has not been laid out yet (for example in onCreate() ) you can setup a ViewTreeObserver.OnGlobalLayoutListener with View.getViewTreeObserver().addOnGlobalLayoutListener() and put the relevant code that needs the view's dimension there. The listener's callback will be called when the layout will have been laid out.

courtesy-Francesco Feltrinelli

You could try to do this.

0

Get the height of the root layout that is loaded by the activity. In case it does not fill the screen put it inside another layout with a height property of fill_parent.

SceLus
  • 1,117
  • 13
  • 18