【问题标题】:Swing and bitmaps on retina displays视网膜显示器上的摆动和位图
【发布时间】:2012-09-08 00:14:23
【问题描述】:

我有一个可以在 OS X 上运行的 Java 桌面应用程序。

现在新款 MacBook Pro 配备了 Retina 显示屏,我很担心:Swing 会如何工作?

当 Java 应用程序同时使用 Swing 组件和一些位图图形(如自定义图标/ImageIcon)时会怎样?

是否应该自动调整所有桌面 Java 应用程序的大小(例如,通过将每个像素翻两番)或者我是否需要创建两个版本的图标集(例如,一个具有 24x24 图标,另一个具有 96x96 图标)并以某种方式确定该应用程序是否在 Retina 显示器上运行?

【问题讨论】:

  • 请编辑您的问题以包含说明您关注的领域的sscce;新的 MacBook Pro 视网膜显示屏所有者可能会发布 screenshot
  • 顺便说一句,您的 24x24 图标只需变成 48x48 图标。 Retina 将每个维度的分辨率加倍,将每个像素变成四个像素。

标签: java swing icons pixel retina-display


【解决方案1】:

这就是我的 Retina macbook '12 上的图标外观:

在 IntelliJ IDEA 11(swing 应用程序)的左侧图标和声称是 retinized 的 IDEA 12 右侧。如您所见,自动调整大小的图标(在左侧)看起来很丑。

据我所知,他们 just like the guys from Chrome team 通过提供双倍大小的图标做到了这一点。

【讨论】:

    【解决方案2】:

    在 Apple 的 Java 6 上,您可以提供同一图像的多个版本。根据屏幕(视网膜与否),选择并绘制一张或另一张图像。

    但是,这些图像必须以特殊方式加载:

    Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension");
    

    例如,如果您的(常规分辨率)图像被称为:“scissor.png”,您必须创建一个高分辨率版本“scissor@2x.png”(在Apple naming conventions 之后)并将这两个图像放在Resources 应用程序包的目录(是的,您需要 bundle your app)。 然后调用:

    Image img = Toolkit.getDefaultToolkit().getImage("NSImage://scissor");
    

    您可以在按钮中使用生成的图像,它会神奇地以正确的分辨率绘制。

    您还可以使用另外两个“技巧”:

    1. 在绘制图像之前在 Graphics2D 对象上使用 (0.5, 0.5) 的 AffineTransform。另见this java-dev message
    2. 使用this hack 以编程方式创建图像的高 dpi 版本

    第一个“技巧”(0.5 缩放)现在也适用于 Oracle 的 Java 7/8。 IE。如果您直接绘制一个缩放为 0.5 到组件的 Graphics 对象的图像,它将在 Retina 显示器上以高分辨率呈现(并且也是原始大小的一半)。

    更新

    从 Java 9 开始,通过MultiResolutionImage 接口对不同分辨率的图像提供了更好的内置支持。详情请见this answer

    【讨论】:

    • 能否请回答者以外的其他人投票或接受它,如果它确实有效,在完成之前无法关闭此问题的重复项。
    • 一旦我捆绑了我的应用程序,那就太好了,但我想在捆绑之前在我的 IDE 中查看正确的行为。你知道怎么做吗?
    • 我刚刚发现我不需要捆绑应用程序来获得正确的行为。在我的 IDE 中,如果我同时拥有旧尺寸和 @2x 尺寸,那么当我从我的 IDE 启动它时它就会起作用。
    【解决方案3】:

    使用 IconLoader 库。它支持 HiDPI 图像http://bulenkov.com/iconloader/ 它还提供了一种处理 HiDPI 图像(绘图等)的方法

    【讨论】:

      【解决方案4】:

      我可以确认您的图像缩放功能适用于 Oracle Java 1.8。我无法让NSImage hack 在 java 1.7 或 1.8 上工作。我认为这只适用于 Mac 的 Java 6...

      除非其他人有更好的解决方案,否则我要做的是:

      创建两组图标。 如果您有一个48pixel 宽度图标,请创建一个48px @normal DPI 和另一个96px2x DPI。将2xDPI 图像重命名为@2x.png 以符合苹果命名标准。

      子类ImageIcon 并称它为RetinaIcon 或其他。 您可以按如下方式测试 Retina 显示屏:

      public static boolean isRetina() {
      
      boolean isRetina = false;
      GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
      
      try {
                    Field field = graphicsDevice.getClass().getDeclaredField("scale");
              if (field != null) {
                  field.setAccessible(true);
                  Object scale = field.get(graphicsDevice);
                  if(scale instanceof Integer && ((Integer) scale).intValue() == 2) {
                      isRetina = true;
                  }
              }
          } 
          catch (Exception e) {
              e.printStackTrace();
          }
          return isRetina;
      }
      

      确保@OverrideImageIcon类的宽度和高度如下:

      @Override
      public int getIconWidth()
      {
          if(isRetina())
          {
              return super.getIconWidth()/2;
          }
          return super.getIconWidth();
      }
      
      @Override
      public int getIconHeight()
      {
          if(isRetina())
          {
              return super.getIconHeight()/2;
          }
          return super.getIconHeight();
      }
      

      一旦您对视网膜屏幕进行了测试并覆盖了您的自定义宽度/高度方法,您就可以自定义 painIcon 方法,如下所示:

      @Override
      public synchronized void paintIcon(Component c, Graphics g, int x, int y) 
      {
          ImageObserver observer = getImageObserver();
      
          if (observer == null) 
          {
              observer = c;
          }
      
          Image image = getImage();
          int width = image.getWidth(observer);
          int height = image.getHeight(observer);
          final Graphics2D g2d = (Graphics2D)g.create(x, y, width, height);
      
          if(isRetina())
          {
              g2d.scale(0.5, 0.5);
          }
          else
          {
      
          }
          g2d.drawImage(image, 0, 0, observer);
          g2d.scale(1, 1);
          g2d.dispose();
      }
      

      我不知道这将如何与多个屏幕一起使用 - 还有其他人可以帮助解决这个问题吗???

      希望这段代码能有所帮助!

      杰森·巴拉克劳。

      以下是使用上述缩放的示例: RetinaIcon is on the left. ImageIcon is on the right

      【讨论】:

        【解决方案5】:

        这是一个解决方案,在苹果菜单中使用图标时也可以使用。那里的图标自动变灰。所以我实现了一个密集绘制的类 DenseIcon:

        public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
            if(getImageObserver() == null) {
                g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), c);
            } else {
                g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), getImageObserver());
            }
        }
        

        如何勾勒我尚未弄清楚的变灰。因此,作为一个组合,我们返回一个低分辨率图像,以便菜单可以进行修改:

        public Image getImage() {
            Image image = getImage0().getScaledInstance(
                    getIconWidth(),
                    getIconHeight(),
                    Image.SCALE_SMOOTH);
            ImageIcon icon = new ImageIcon(image, getDescription());
            return icon.getImage();
        }
        

        您可以在gist 上找到完整课程的代码。您需要使用两倍大小的图像的 URL 来实例化图标类。适用于 2K 显示器。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-24
          • 1970-01-01
          • 1970-01-01
          • 2015-07-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多