我正在尝试设置Java AWT应用程序的图标,以使其在Windows 10任务栏上以本机分辨率呈现(包括将桌面缩放比例设置为100%以上时)。似乎默认情况下,如果可执行文件嵌入了包含多个尺寸的图标,则Windows似乎会选择一个比任务栏图标的实际尺寸大的尺寸并将其缩小(以100%的比例将32像素图标的尺寸调整为24,即使提供了24像素的图标,其他比例也是如此。)
我已经解决了C ++ MFC应用程序的此问题,只需将正确大小的图标作为资源加载并向窗口发送WM_SETICON消息,这将在任务栏和alt-tab对话框上产生一个漂亮的尖锐图标。
smallIcon = (HICON)LoadImage( myInstance, MAKEINTRESOURCE(smallIconRes), IMAGE_ICON, smallIconSize, smallIconSize, LR_DEFAULTCOLOR );
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)smallIcon);
bigIcon = (HICON)LoadImage( myInstance, MAKEINTRESOURCE(bigIconRes), IMAGE_ICON, bigIconSize, bigIconSize, LR_DEFAULTCOLOR );
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)bigIcon);
该方法似乎不适用于Java应用程序-将wParam设置为ICON_SMALL的WM_SETICON消息可以正常工作,但忽略与ICON_BIG等效的消息。
如果我尝试使用Java的API来设置图标,则可以这样做
List<Image> icons = new ArrayList<Image>();
icons.add(windowIcons.getIcon(20)); // small icons are 20x20 pixels
icons.add(windowIcons.getIcon(30)); // large are 30x30 at 125% scale
setIconImages(icons);
使用了正确的图标,但是它显得模糊,好像有些东西将其调整为“预期”大小,然后又重新调整了大小。左是它的外观,右是图标文件的内容。
所以,我的问题是:我可以在该Java应用程序中执行什么操作,以使Windows呈现我在任务栏上提供的图标而不会缩放它并模糊细节?
答案 0 :(得分:2)
这不是您想要的答案,但这在JDK级别上似乎是个问题。
窗口图标由sun.awt.windows.WWindowPeer
类处理,该类依次进行了一些本机方法调用,但是在源代码中可以看到很多足以指出问题的地方。请阅读重要的here位。
基本上,无论提供多少图标图像大小,它都只会挑选两种大小-WWindowPeer.getSysIconWidth()
和getSysSmIconWidth()
-传递给本机setIconImagesData()
方法。 / p>
getSysIconWidth()
和getSysSmIconWidth()
方法也是本机方法,但是可以直接检查它们的返回值:
JFrame frame = new JFrame();
runOnPeer(frame, "getSysIconWidth");
runOnPeer(frame, "getSysIconHeight");
runOnPeer(frame, "getSysSmIconWidth");
runOnPeer(frame, "getSysSmIconHeight");
private void runOnPeer(JFrame frame, String methodName) {
//JDK8 style
//ComponentPeer peer = frame.getPeer();
//JDK11 style
Field peerField = Component.class.getDeclaredField("peer");
peerField.setAccessible(true);
Object peer = peerField.get(frame);
Method method = Class.forName("sun.awt.windows.WWindowPeer")
.getDeclaredMethod(methodName);
method.setAccessible(true);
System.out.println(methodName + "()=" + method.invoke(peer));
}
...在Windows 10上返回此值...
getSysIconWidth()=32
getSysIconHeight()=32
getSysSmIconWidth()=16
getSysSmIconHeight()=16
正如您所说,很明显,这些图像大小之一正在按任务栏缩放。
答案 1 :(得分:2)
在sun.awt.SunToolkit中确实存在一个称为getScaledIconImage()
的缩放功能,该功能在设置图标时始终使用。您必须绕过此功能才能获得未混淆的图标。因此,您需要的是java.awt.Window.setIconImages()
方法的替代品。
提供了几个图标图像Icon16x16.png,Icon24x24.png等。这是一个customSetIconImages()
的示例,该示例将清晰的24x24像素图标放在Windows 10的任务栏中。
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.ImageIcon;
import java.awt.peer.WindowPeer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@SuppressWarnings("serial")
public class MyFrame extends Frame implements WindowListener {
final Image i16, i24, i32, i48;
MyFrame() throws Exception {
i16 = Toolkit.getDefaultToolkit().getImage("Icon16x16.png");
i24 = Toolkit.getDefaultToolkit().getImage("Icon24x24.png");
i32 = Toolkit.getDefaultToolkit().getImage("Icon32x32.png");
i48 = Toolkit.getDefaultToolkit().getImage("Icon48x48.png");
addWindowListener(this);
setSize(500,300);
setTitle("Unaliased icon example");
setLayout(new FlowLayout());
setVisible(true);
}
public synchronized void customSetIconImages(java.util.List<Image> icons) throws Exception {
Field windowIcons = Class.forName("java.awt.Window").getDeclaredField("icons");
windowIcons.setAccessible(true);
windowIcons.set(this, new ArrayList<Image>(icons));
if (getPeer() != null)
updateIconImages(i24, 24, 24, i24, 24, 24);
firePropertyChange("iconImage", null, null);
}
public void updateIconImages(Image big, int bw, int bh, Image small, int sw, int sh) throws Exception {
DataBufferInt iconData = getUnscaledIconData(big, bw, bh);
DataBufferInt iconSmData = getUnscaledIconData(small, sw, sh);
WindowPeer peer = (WindowPeer) getPeer();
Method setIconImagesData = Class.forName("sun.awt.windows.WWindowPeer").getDeclaredMethod("setIconImagesData", int[].class, int.class, int.class, int[].class, int.class, int.class);
setIconImagesData.setAccessible(true);
setIconImagesData.invoke(peer, iconData.getData(), bw, bh, iconSmData.getData(), sw, sh);
}
public static DataBufferInt getUnscaledIconData(Image image, int w, int h) {
Image temporary = new ImageIcon(image).getImage();
BufferedImage buffImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffImage.createGraphics();
g2d.drawImage(temporary, 0, 0, null);
g2d.dispose();
Raster raster = buffImage.getRaster();
DataBuffer buffer = raster.getDataBuffer();
return (DataBufferInt) buffer;
}
@Override
public void windowOpened(WindowEvent arg0) {
try {
customSetIconImages(Arrays.asList(i24));
} catch (Exception e) {
System.err.println(e.getClass().getName()+" "+e.getMessage());
}
}
@Override
public void windowActivated(WindowEvent arg0) {
}
@Override
public void windowClosed(WindowEvent arg0) {
}
@Override
public void windowClosing(WindowEvent arg0) {
dispose();
}
@Override
public void windowDeactivated(WindowEvent arg0) {
}
@Override
public void windowDeiconified(WindowEvent arg0) {
}
@Override
public void windowIconified(WindowEvent arg0) {
}
public static void main(String args[]) throws Exception {
MyFrame fr = new MyFrame();
}
}
正如@ df778899所说,在sun.awt.windows.WWindowPeer内部,有四个私有本机方法,您可以调用它们来确定系统图标的大小。您可以将这些方法返回的信息与您自己的版本getScaledIconImage()
结合使用,该版本执行不混叠或不按照您的意愿进行。
最后,请注意,这是一个非常肮脏的技巧,仅用于获取未混淆的图标。我仅在Java 8和Windows 10中进行过测试。而且很有可能在新版本的Java中不起作用。