【问题标题】:How to find real display density (DPI) from Java code?如何从 Java 代码中找到真实显示密度 (DPI)?
【发布时间】:2016-11-03 10:52:08
【问题描述】:

我要做一些低级渲染的工作,但我需要知道真实的显示 DPI 才能使所有内容都具有正确的大小。

我找到了一种方法来做到这一点: java.awt.Toolkit.getDefaultToolkit().getScreenResolution() — 但它在带有“retina”显示的 OS X 上返回不正确的结果,它是真实 DPI 的 1/2。 (在我的情况下应该是 220,但它是 110)

所以要么必须提供其他更正确的 API,要么我需要为 OS X 实现一个 hack——以某种方式找出当前显示器是否是“视网膜”。但我也找不到任何方法来查询这些信息。有 this answer 但在我的机器上 Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor") 只返回 null。

我该怎么做?

【问题讨论】:

    标签: java macos retina-display dpi hidpi


    【解决方案1】:

    目前看来可以从java.awt.GraphicsEnvironment 获得它。这是在最新的 JDK (8u112) 上工作的注释代码示例。

    // find the display device of interest
    final GraphicsDevice defaultScreenDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
    
    // on OS X, it would be CGraphicsDevice
    if (defaultScreenDevice instanceof CGraphicsDevice) {
        final CGraphicsDevice device = (CGraphicsDevice) defaultScreenDevice;
    
        // this is the missing correction factor, it's equal to 2 on HiDPI a.k.a. Retina displays
        final int scaleFactor = device.getScaleFactor();
    
        // now we can compute the real DPI of the screen
        final double realDPI = scaleFactor * (device.getXResolution() + device.getYResolution()) / 2;
    }
    

    【讨论】:

    • 这取决于平台。在 Linux 上,它是 X11GraphicsDevice,甚至是 sun 类,通常对用户隐藏。
    【解决方案2】:

    这是一个来自@sarge-borsch 的示例,它不会在 Windows 和 Linux 上引发编译错误。

    public static int getScaleFactor() {
        try {
            // Use reflection to avoid compile errors on non-macOS environments
            Object screen = Class.forName("sun.awt.CGraphicsDevice").cast(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice());
            Method getScaleFactor = screen.getClass().getDeclaredMethod("getScaleFactor");
            Object obj = getScaleFactor.invoke(screen);
            if (obj instanceof Integer) {
                return ((Integer)obj).intValue();
            }
        } catch (Exception e) {
            System.out.println("Unable to determine screen scale factor.  Defaulting to 1.");
        }
        return 1;
    }
    

    【讨论】:

    • 我最后也使用了类似的东西,只是忘记将其添加到答案中。
    • 您不需要nulls,也不需要Class.forClass 表达式:您可以直接在getDefaultScreenDevice() 实例上调用反射。但要注意 default 屏幕与 current 屏幕不同:最好将 Component 的图形配置绘制在上面,然后从那里。这样,在多个屏幕上移动对话框将正确更新渲染。
    • 我在发布后注意到了编译警告。示例在没有 null 参数的情况下更新。关于“默认屏幕”,我的示例用于系统托盘(无父组件),因此我认为它对于我的(非常具体的)用例是正确的。关于Class.forClass,我不确定您的建议是什么,因此作为新答案或对此答案的改进可能有意义。
    • @tresf 你根本不需要演员表:直接使用GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
    • 谢谢。如果在非苹果设备上使用,如果异常消息在 Class.forName 而不是 Method.invoke 上失败,对程序员来说不是更直观吗?
    猜你喜欢
    • 2013-09-10
    • 2017-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 2010-09-10
    • 2021-08-06
    相关资源
    最近更新 更多