如何通过编程获取Android屏幕大小?

135
我如何以编程方式找到我的屏幕尺寸, 使用触摸事件和视图测量/布局使用的单位?换句话说,我想要底部右下角屏幕的坐标, 在由触摸事件“getRawX() / getRawY()”和“View.getLocationOnScreen()”使用的坐标系中。
我不确定所需事件/视图单位是否为“像素”, 因为显然在手机上有几种“像素”概念, 它们没有形成一致的故事。
我在stackoverflow和其他地方看到这个问题被提出和回答了很多次, 但是没有一个答案在我的手机(Droid 4、Android 4.1.2)的所有模式下都有效。

(哇!)

这是用于库代码的,需要在应用程序处于“屏幕兼容模式”(即目标SDK版本<=10在清单中)或不处于该模式时正常工作, 我也不想对状态栏或任何类似的屏幕装饰的位置、大小或存在做出任何假设。


以下是事实:

  1. 我的手机 (运行 Android 4.1.2 的 droid 4) 物理像素为 540x960, 即小彩色发光点。

  2. 从触摸事件和 View 测量来看,所需单位下的屏幕大小为 当应用程序处于屏幕兼容模式时为 360x640, 当应用程序不处于屏幕兼容模式时为 540x960。 这些是我需要以编程方式找到的数字, 而不必通过处理触摸事件或查找视图来找到它们, 但我非常难以找到任何API会返回这些数字。

  3. 以各种方式获取的 Display 和 DisplayMetrics 对象都声称屏幕大小为 540x960“像素” (无论是否处于屏幕兼容模式)。 具体来说,以下所有内容始终显示 540x960: DisplayMetrics.{width,height}Pixels、 Display.getSize()、 Display.getRealSize()、 Display.get{Width,Height}()

  4. 以各种方式获取的 Configuration 对象都说屏幕{Width,Height}Dp = 360x614 (无论是否处于屏幕兼容模式)。 我不认为这代表整个屏幕,因为宽高比是错误的。 (我认为这是整个屏幕减去状态栏; 我需要整个屏幕。) 我认为可以肯定地说整个屏幕是 360x640 dp, 尽管我不知道任何API返回那个 640。

  5. 以各种方式获取的 DisplayMetrics 声明“密度”是 在屏幕兼容模式下为 1.0f, 不在屏幕兼容模式下为 1.5f。

  6. 活动的 getWindow().getAttributes().{width,height} 并没有帮助,因为它通常包含 MATCH_PARENT 而不是实际大小。 但是我显然可以从活动的以下位置获得所需的答案 getWindow().getDecorView().getMeasured{Width,Height}() (这实际上令人惊讶, 因为活动窗口的装饰视图看起来不像占据整个屏幕; 它看起来像是占据除状态栏之外的屏幕)。 但是我不想依赖这个,因为如果窗口被调整大小 (软键盘出现? 有人调用 window.setAttributes()? 或者我根本就不在一个 Activity 中), 这显然会完全错误。

我理解以下公式应该成立: 像素 = dp * 密度 当 处于屏幕兼容模式时,这似乎符合所有报告的数字 ((3),(4),(5) 上述): 540x960 = 360x640 * 1.5 但在屏幕兼容模式下,它不成立: 540x960 != 360x640 * 1 因此,有些地方出了问题。
我认为最简单的解释是, 上述 (3) 中列出的方法在屏幕兼容模式下给出了错误的 "像素" 答案 - 即,它们旨在返回 360x640 "像素",但却错误地返回了 540x960。 但可能还有其他看法。
无论如何,从上述谜题中得到所需的数字,无论处于哪种模式,都是一个棘手的难题。 我已经找到了一种方式,在我的手机上两种模式下都有效, 但它非常迂回, 它依赖于两个仍然相当脆弱的假设 (如下面的代码注释所述)。
这是我的步骤:
/** get screen size in "pixels", i.e. touchevent/view units.
* on my droid 4, this is 360x640 or 540x960
* depending on whether the app is in screen compatibility mode
* (i.e. targetSdkVersion<=10 in the manifest) or not. */
public void getScreenSizePixels(int widthHeightInPixels[/*2*/])
{
    Resources resources = getResources();
    Configuration config = resources.getConfiguration();
    DisplayMetrics dm = resources.getDisplayMetrics();
    // Note, screenHeightDp isn't reliable
    // (it seems to be too small by the height of the status bar),
    // but we assume screenWidthDp is reliable.
    // Note also, dm.widthPixels,dm.heightPixels aren't reliably pixels
    // (they get confused when in screen compatibility mode, it seems),
    // but we assume their ratio is correct.
    double screenWidthInPixels = (double)config.screenWidthDp * dm.density;
    double screenHeightInPixels = screenWidthInPixels * dm.heightPixels / dm.widthPixels;
    widthHeightInPixels[0] = (int)(screenWidthInPixels + .5);
    widthHeightInPixels[1] = (int)(screenHeightInPixels + .5);
}

有没有更好/更简洁的方法来查找屏幕大小?

32
好问题,并展示了很多研究努力。 - Alex Gittemeier
1
文档中,解释了兼容模式将停止在旧设备上执行“缩放以适应”操作。然而,它并没有清楚地解释“它是如何工作的”,从您的观察来看,我认为它们只是关闭了密度缩放,即1px变成了1dp。这种变化可能仅存在于渲染部分,并且DisplayMetrics从未更新此类更改。已经有像这样的问题被提出。 - S.D.
@user117 - 有趣。至少 DisplayMetrics 的一部分得到了更新——密度变为1.0。但似乎 widthPixels/heightPixels 没有被更改以匹配这种变化。关于引用的问题,看起来行为已恢复到3.1的行为(即我看到 heightPixels 包括状态栏......但似乎使用了错误的单位)。 - Don Hatch
@DonHatch 多谢,我实际上是想夸赞一个在StackExchange上不那么受欢迎的周到问题。请注意,问题本身并不“真正”,在回答中却什么也没有得到,说明受众对该主题的知识水平很低。现在,我的问题被关闭了,一个接着一个地被认为“不真实”,我的幽默感也可能在消失,但这似乎说明了“可爱”的回答是这里真正关注的内容......当然,我给你点了赞 :) - mlvljr
顺便说一句!这是我怀疑的东西:http://michael.richter.name/blogs/why-i-no-longer-contribute-to-stackoverflow/(特别是看他的(1)点,关于快速制作答案 - 实际上就是这个问题的情况!)。无论如何,新年快乐(我的还有11分钟到来)! :) - mlvljr
小心“一劳永逸”的解决方案:https://www.youtube.com/watch?v=0SYpUSjSgFg - ThomasW
10个回答

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

现在你可以用像素来测量屏幕大小,这是一个比厘米更好的测量单位,因为所有的按钮、文本视图等都是用这个单位来测量的。这也是我通常使用的单位。


4
根据Op(特别是第3点),在屏幕兼容模式下这不会是错误的吗? - Tom
这是错误的。这是UiAutomator2获取屏幕尺寸的方式,但是是错误的。 - undefined

9

通过使用DisplayMetrics,您可以获取任何设备屏幕的高度和宽度。请注意,宽度和高度会受到旋转的影响。 以下是代码。

DisplayMetrics metrics; 
int width = 0, height = 0;

在你的onCreate方法中。
 metrics = new DisplayMetrics();        
 getWindowManager().getDefaultDisplay().getMetrics(metrics);

 height = Math.min(metrics.widthPixels, metrics.heightPixels); //height
 width = Math.max(metrics.widthPixels, metrics.heightPixels); //width

试试这个。


4
不行,正如我在(3)中所说的那样,当处于屏幕兼容模式时,metrics.{width,height}Pixels并不能给出正确的答案。 - Don Hatch
我还发现:如果屏幕旋转,lite metrics.heightPixels可能会给出宽度,反之亦然! - JohnyTex

8
在您的第6点中,您提到DecorView似乎提供了您想要的内容,但您认为它不可靠。我相信这是您能够接近的最近的地方。根据Window.getDecorView文档的说明:
检索顶层窗口装饰视图(包含标准窗口框架/装饰和客户端内容的内部),可以将其作为窗口添加到窗口管理器中。
状态栏是其中提到的窗口装饰的一部分。软件键盘和其他覆盖层将接收到自己的窗口,因此它们不应干扰相关值。确实不是精确的显示度量,但如果您的应用程序是全屏的,则始终应通过兼容性转换器过滤全屏大小。其他应用程序无法访问您的应用程序的Window,因此无需担心其他应用程序在您不看时更改属性。
希望对您有所帮助。

5

我应该把这个当作评论,但是我的声望还不够。
这可能不是完全正确的答案,但是我在DisplayMetrics方法中交换了高度和宽度时获得了我的尺寸。

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

如我所说,这可能不是正确的,但我不知道为什么它对我起作用了。


这是因为我认为您的屏幕已经旋转了。 - JohnyTex
这不是相信我。 - otaku

3

如果您使用的是API 17或更高版本,请查看显示中的getRealMetrics和getRealSize。

对于API级别14、15和16,请参阅此处


3

我使用勾股定理来计算Android手机/平板电脑屏幕的对角线尺寸,同样的原理也适用于iPhone或Blackberry屏幕。

DisplayMetrics met = new DisplayMetrics();                
this.getWindowManager().getDefaultDisplay().getMetrics(met);// get display metrics object
String strSize = 
new DecimalFormat("##.##").format(Math.sqrt(((met.widthPixels / met.xdpi) *
(met.widthPixels / met.xdpi)) +
((met.heightPixels / met.ydpi) * (met.heightPixels / met.ydpi))));
// using Dots per inches with width and height

毕达哥拉斯一定是个天才,因为他很多年前就懂得了智能手机编程 :p


1
我使用以下方法来获取设备的宽度:
public static int getDeviceWidth(Context context){
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    int width=display.getWidth();
    return width;
}

此方法已被弃用,请使用displaymetrics方法。 - Simon

0
这对我很有效:

int width = Resources.getSystem().getDisplayMetrics().widthPixels;

int height = Resources.getSystem().getDisplayMetrics().heightPixels;

我用这个来更新我的水平RecyclerView中项目的宽度(根据我的需求)。 希望能帮到别人!


0

我认为这个函数可以帮助你轻松获取Android屏幕尺寸的宽度和高度。该函数将以整数数组的形式返回宽度和高度,如下所示:

private int[] getScreenSIze(){
        DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        int h = displaymetrics.heightPixels;
        int w = displaymetrics.widthPixels;

        int[] size={w,h};
        return size;

    }

并在你的创建方法中输出如下所示的宽度和高度

 int[] screenSize= getScreenSIze();
        int width=screenSize[0];
        int height=screenSize[1];
        screenSizes.setText("Phone Screen sizes \n\n  width = "+width+" \n Height = "+height);

下载源代码并在你的Android Studio上进行测试


-1

对于这个与Kotlin相关的解决方案,请尝试以下方法:

private fun yourFunction(){
   val metrics = DisplayMetrics()
   windowManager.defaultDisplay.getMetrics(metrics)
   val width = metrics.widthPixels
   val height = metrics.heightPixels
}

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