Java 视网膜显示器上的Swing和位图
我有一个Java桌面应用程序,可以在OSX上运行 现在新的MacBookPro有了一个视网膜显示屏,我很担心:关于Swing,它将如何工作 如果Java应用程序同时使用Swing组件和一些位图图形(如自定义图标/ImageIcon),该怎么办Java 视网膜显示器上的Swing和位图,java,swing,icons,pixel,retina-display,Java,Swing,Icons,Pixel,Retina Display,我有一个Java桌面应用程序,可以在OSX上运行 现在新的MacBookPro有了一个视网膜显示屏,我很担心:关于Swing,它将如何工作 如果Java应用程序同时使用Swing组件和一些位图图形(如自定义图标/ImageIcon),该怎么办 是否所有桌面Java应用程序都会自动调整大小(例如,通过将每个像素增加四倍),还是我需要创建两个版本的图标集(例如,一个具有24x24图标,另一个具有96x96图标)并以某种方式确定应用程序正在视网膜显示器上运行?这是我的视网膜macbook'12上的图标
是否所有桌面Java应用程序都会自动调整大小(例如,通过将每个像素增加四倍),还是我需要创建两个版本的图标集(例如,一个具有24x24图标,另一个具有96x96图标)并以某种方式确定应用程序正在视网膜显示器上运行?这是我的视网膜macbook'12上的图标的外观: 在IntelliJ IDEA 11(swing应用程序)的左侧图标上,在IDEA 12的右侧图标上,该图标被重新命名。正如您所看到的,自动调整大小的图标(在左侧)看起来非常难看
据我所知,他们通过提供两倍大小的图标而成功。在苹果的Java 6上,您可以提供同一图像的多个版本。根据屏幕(视网膜与否),选择并绘制一幅或另一幅图像 但是,必须以特殊方式加载这些图像:
Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension");
例如,如果您的(常规分辨率)图像名为:“剪刀.png”,则必须创建高分辨率版本”scissor@2x.png“(遵循)并将这两个图像放置在应用程序包的参考资料
目录中(是的,您需要)。
然后打电话:
Image img = Toolkit.getDefaultToolkit().getImage("NSImage://scissor");
您可以在按钮中使用生成的图像,它将以正确的分辨率神奇地绘制出来
您还可以使用另外两个“技巧”:
也就是说,如果您以0.5的比例直接绘制一幅图像到组件的图形对象,它将在视网膜显示器上以高分辨率呈现(也将以其原始大小的一半呈现)。使用IconLoader库。它支持HiDPI图像它还提供了一种处理HiDPI图像(绘图等)的方法。我可以确认,在Oracle Java 1.8上,图像的缩放功能是有效的。我无法让
NSImage
hack在Java1.7或1.8上工作。我认为这只适用于Mac的Java6
除非其他人有更好的解决方案,否则我所做的是:
创建两组图标。
如果您有一个48像素
宽度图标,请创建一个48px
@normalDPI
,另一个位于96px
的2xdpi
。将2xDPI
图像重命名为@2x.png
,以符合苹果的命名标准
子类ImageIcon
并将其命名为RetinaIcon
或其他任何名称。
您可以按如下方式测试视网膜显示:
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;
}
@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();
}
@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();
}
确保@覆盖新图像图标的宽度和高度,如下所示:
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;
}
@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();
}
@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();
}
一旦您对视网膜屏幕进行了测试,并且覆盖了自定义宽度/高度方法,您可以按如下方式自定义painIcon
方法:
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;
}
@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();
}
@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();
}
我不知道这将如何与多个屏幕,但-有没有其他人可以帮助呢
希望这段代码能有所帮助
杰森·巴拉克劳夫
下面是使用上述缩放的示例:
这里有一个解决方案,当在apple菜单中使用图标时也可以使用。在那里,图标会自动变为灰色。因此,我实现了一个类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();
}
您可以在此处找到完整类的代码。您需要使用两倍大小的图像的URL实例化icon类。适用于2K显示器。请编辑您的问题,包括一个说明您关注领域的问题;新的MacBook Pro视网膜显示器所有者可能会发布一个。顺便说一句,您的24x24图标只需要变成48x48图标。视网膜将每个维度的分辨率提高一倍,将每个像素转换为四个像素。请回答者以外的其他人对此进行投票或接受,如果它确实有效,则在完成此操作之前,无法关闭此问题的副本。