如何在Java中检测Retina显示屏?

8

如何在Java中检测用户是否拥有Retina显示屏?我已经知道使用Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor")来检测比例因子,但是Java不允许我将返回的值转换为int类型。我想知道如何将其转换为int类型,或者另一种检测Retina显示屏的方法。


你如何在具有Retina显示屏的设备上运行Java?或者我是指,你正在运行哪个Java... - Elliott Frisch
你使用了什么东西,导致无法将返回值转换为整数?(应该是浮点数)。 - brandonscript
我正在使用带有Retina显示屏的Macbook Pro运行它。当我运行代码并尝试将返回值强制转换为“整数”时,它会给出一个错误,指出“主线程中的异常java.lang.ClassCastException:java.lang.Float无法转换为java.lang.Integer”。 - Tyler
不要那样做!只需使用 intValue() - Elliott Frisch
Java不允许我这样做,因为“类型Object未定义方法intValue()”。 - Tyler
3个回答

5

请注意,用户可能拥有多个显示器!在这种情况下,“检测Retina显示屏”是什么意思?

对于大多数情况,您需要将图像呈现在GUI组件上。因此,您需要检测组件所在的显示器

幸运的是,java.awt.Component具有一个getGraphicsConfiguration方法,可以提供我们必要的信息。

然而,Java 8(和7)和Java 9需要不同的处理方式:Java 9直接通过图形设备的默认转换公开所需的信息。 Java 7和8也公开这个转换,但它始终设置为身份转换(即没有转换),即使是Retina显示屏也是如此。

对于Java < 9,我们需要使用反射来查询OpenJDK类中实现Mac图形的特定字段。

以下类实现了Retina显示屏的必要检查,并适用于Java 8和Java 9。Java 7可能需要进行微小更改,但我没有测试过。

package me.klmr.ui;

import java.awt.*;
import java.lang.reflect.Method;

public final class Device {
    private enum JavaVersion {
        V8,
        V9
    }

    private static final JavaVersion JAVA_VERSION = getJavaVersion();

    private static JavaVersion getJavaVersion() {
        final String versionString = System.getProperty("java.version");
        if (versionString.startsWith("1.8")) return JavaVersion.V8;
        if (versionString.startsWith("9.")) return JavaVersion.V9;
        throw new RuntimeException("Unsupported Java version");
    }

    public static GraphicsConfiguration getCurrentConfiguration(final Component component) {
        final GraphicsConfiguration graphicsConfiguration = component.getGraphicsConfiguration();
        if (graphicsConfiguration == null) {
            return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        } else {
            return graphicsConfiguration;
        }
    }

    public static GraphicsDevice getCurrentDevice(final Component component) {
        final GraphicsConfiguration graphicsConfiguration = component.getGraphicsConfiguration();
        if (graphicsConfiguration == null) {
            return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        } else {
            return graphicsConfiguration.getDevice();
        }
    }

    public static boolean isOnRetinaDisplay(final Component component) {
        switch (JAVA_VERSION) {
            case V8: return isOnRetinaDisplayJava8(component);
            case V9: return isOnRetinaDisplayJava9(component);
            default: throw new AssertionError("Unreachable");
        }
    }

    public static double getDisplayScalingFactor(final Component component) {
        switch (JAVA_VERSION) {
            case V8: return getDisplayScalingFactorJava8(component);
            case V9: return getDisplayScalingFactorJava9(component);
            default: throw new AssertionError("Unreachable");
        }
    }

    private static boolean isOnRetinaDisplayJava8(final Component component) {
        final GraphicsDevice device = getCurrentDevice(component);
        try {
            final Method getScaleFactorMethod = device.getClass().getMethod("getScaleFactor");
            final Object scale = getScaleFactorMethod.invoke(device);
            return scale instanceof Integer && ((Integer) scale).intValue() == 2;
        } catch (ReflectiveOperationException e) {
            return false;
        }
    }

    private static boolean isOnRetinaDisplayJava9(final Component component) {
        return ! getCurrentConfiguration(component).getDefaultTransform().isIdentity();
    }

    private static double getDisplayScalingFactorJava8(final Component component) {
        return isOnRetinaDisplayJava8(component) ? 2.0 : 1.0;
    }

    private static double getDisplayScalingFactorJava9(final Component component) {
        return getCurrentConfiguration(component).getDefaultTransform().getScaleX();
    }
}

实际上,将对话框从一个屏幕移动到另一个屏幕会导致组件重新渲染。如果组件的渲染代码使用上述类来查找正确的分辨率,则无论它们当前位于哪个显示器上,它们都将正确地呈现。


4
我会这样获取值 -
public static boolean hasRetinaDisplay() {
  Object obj = Toolkit.getDefaultToolkit()
      .getDesktopProperty(
          "apple.awt.contentScaleFactor");
  if (obj instanceof Float) {
    Float f = (Float) obj;
    int scale = f.intValue();
    return (scale == 2); // 1 indicates a regular mac display.
  }
  return false;
}

非常感谢,这个可以用。我想我只是需要那个if语句并在将其转换为整数之前将其转换为浮点数。 - Tyler
1
我可以问一下你使用视网膜显示屏的价值是什么吗?我将重新编写答案,形成一个完整的实用方法... - Elliott Frisch
如果是视网膜显示屏且计算机是Mac,返回2;如果不是视网膜显示屏但计算机是Mac,返回1;否则返回null。 - Tyler
1
有一个能够与Oracle JDK配合使用的工具会很不错,因为新版本现在支持Retina显示屏。 - eye_mew
4
我最新的Oracle JDK无法使用,obj只是空的。然而,我找到了另一种获取该信息的方法,并编写了这个答案 - Display Name
显示剩余4条评论

3

对于Java 9,也可以这样实现:

    public static boolean isMacRetinaDisplay() {
        final GraphicsConfiguration gfxConfig = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        final AffineTransform transform = gfxConfig.getDefaultTransform();
        return !transform.isIdentity();
    }

你可以检查变换的比例因子,如果它等于2,则可以选择使用非Retina屏幕。

这个有什么特别的Java 9吗?在Java 7中编译(还没有运行它,因为没有傻瓜视网膜Mac)。 - NateS
1
@NateS API已经存在,但在Java 9之前返回错误的值:Java 7和Java 8即使在Retina显示器上也使用恒等变换,而“ScaleX”和“ScaleY”值始终为1.0,而不管实际硬件显示比例值。 - Konrad Rudolph

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