在Android中获取屏幕高度

18
如何在Android中获取可用屏幕高度?我需要减去状态栏/菜单栏或其他可能显示在屏幕上的装饰物的高度,并且需要适用于所有设备。此外,我需要在onCreate函数中知道这个高度。我已经尝试过其他人提供的解决方案,但都不起作用。以下是我尝试过的一些方法:
我已在API 7-17上测试了此代码。不幸的是,在API 13上,水平和垂直方向都有额外的空间,在API 10、8和7上,底部水平和垂直方向都没有足够的空间。(我没有在过时的API上测试):
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);

这并未考虑状态栏/菜单栏:

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

这也不行:

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

也不是这个:

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

这不起作用:

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
}
我随后使用以下代码从屏幕高度中减去状态栏和菜单栏的高度:

然后我使用以下代码从屏幕高度中减去状态栏和菜单栏的高度:

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;

在API 17上,它能正确计算肖像模式下状态栏和菜单栏的高度,但是在横向模式下不行。在API 10上,它返回0。我需要它在所有设备上或最低API 7上都能够理想地工作。


如果你在onCreate方法中真的需要精确的尺寸,那么你可能做错了什么。使用全局布局监听器,在布局阶段完成后运行你的操作是正确的做法,就像@Carnal的回答一样。在全局布局回调中,你应该使用getMeasuredHeight而不是getHeight来获取View的高度。 - Mikooos
1
@DanBray - 请查看我提供的解决方案。它绝对有效。 - Karthik Balakrishnan
任何通过使用 OnGlobalLayoutListener 或将 Runnable 发送到根视图的消息队列来延迟获取尺寸的方法都应该可以工作。 - MH.
我会获取装饰视图并根据DecorView进行所有计算。 - Olsi Saqe
8个回答

7

如果您想要以像素为单位显示尺寸,可以使用getSize

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

如果您不在Activity中,可以通过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;

getSize在API 13中引入之前,您可以使用现在已弃用的getWidth和getHeight方法:

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

参考自:获取屏幕像素尺寸


6
工作解决方案:
创建两个虚拟视图:
 <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" />

现在,您应该在这两个视图上调用getLocationOnScreen(int [])来接收它们的位置。此函数返回一个包含2个整数的数组。第一个是x,第二个是y。我们只需要y。还请注意,仅在所有视图创建完毕并退出onCreate方法后才会返回正确的值。
我在从ViewPager调用的常规片段上测试了此代码。
该代码位于我的片段的onCreateView方法中。我仍然需要使用postDelayed来获取正确的值。
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);

我有一部 Nexus 4 手机。
在普通模式下(状态栏和软按键可见):
07-05 02:28:23.367      565-565/com.torcellite.xx D/xx: topY: 50 BottomY:1182

在全屏模式下(不显示状态栏或软按钮):
07-05 02:29:17.360      707-707/com.torcellite.xx D/xx: topY: 0 BottomY:1280

现在你需要做的就是减去。

1
你能发一下logcat吗?如果是Activity的话,请尝试将我的代码放在onResume中。 - Karthik Balakrishnan
1
我认为我知道它为什么会崩溃。顶部和底部的两个视图必须在您的活动中设置内容视图的同一个XML文件中。因此,如果是main_activity.xml,则视图应放入该XML文件中,而不是新建一个文件。 - Karthik Balakrishnan
1
好的,那你没有设置内容视图吗?那么你应该在你的代码中动态膨胀这些视图。 - Karthik Balakrishnan
1
你需要将视图添加到“布局”中。 - Karthik Balakrishnan
1
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/32985/discussion-between-torcellite-and-dan-bray - Karthik Balakrishnan
显示剩余8条评论

2

用于显示

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

状态栏

public int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

这是我尝试过的其中一件事情,但它并不适用于所有设备。我在我的问题中展示了这一点。 - Dan Bray
@DanBray - 请看一下我的解决方案,它适用于所有设备。 - Karthik Balakrishnan

2

我没有尝试过不同的API,但我正在使用这些值来判断屏幕键盘是否弹出。也许你可以尝试这个方法,并使用类似的方式来获取所需的宽度?

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;

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

onCreate中使用此监听器(自API 1可用)。您需要在xml布局中将父项分配id @+id/root。由于此侦听器在布局已定位到视图时进行回调,因此没有办法使大小返回0。


如果我使用XML文件,这可能会起作用。但是,我没有使用XML文件来进行布局。实际上,我的整个程序中都没有任何XML文件,因为我更希望它能够动态工作。 - Dan Bray
我使用final View view = getWindow().getDecorView().getRootView();来获取视图,而不是final View view = findViewById(R.id.root);。不幸的是,我仍然无法获取减去任何装饰的屏幕高度。screenHeight = view.getMeasuredHeight();或者screenHeight = view.getHeight();不能得到排除状态栏或菜单栏后的高度。 - Dan Bray
这个不能在onCreate中运行。它只会在布局被扩展后触发,而这是在onCreate执行之后发生的。 - Johann
2
在你不理解的情况下给我负评之前,你应该先了解一下OnGlobalLayoutListener。 - Carnal

0
如何将子类化的View设置为Activity的根视图。使此视图填充屏幕(它不会超过状态栏),并重写onSizeChanged()方法。存储这些值并编写一个getter方法。

0
这可能不是你问题的答案,但是知道这个信息可能会有用(当我遇到这个问题时我自己也在寻找它)。如果你需要一个视图的尺寸,但是你的代码在布局还没有被设置好的时候就已经执行了(例如在onCreate()中),你可以使用View.getViewTreeObserver().addOnGlobalLayoutListener()来设置一个ViewTreeObserver.OnGlobalLayoutListener,并将需要视图尺寸的相关代码放在那里。当布局被设置好后,监听器的回调函数将被调用。
感谢Francesco Feltrinelli提供的信息。
你可以尝试这样做。

0

获取由活动加载的根布局的高度。如果它没有填满屏幕,则将其放置在另一个高度属性为fill_parent的布局中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接