是否可以在Windows 10中为AWT应用程序提供清晰的任务栏图标

MLd*_*deS 13 java icons awt windows-10

我正在尝试设置Java AWT应用程序的图标,以使其在Windows 10任务栏上以本机分辨率呈现(包括将桌面缩放比例设置为100%以上时)。似乎默认情况下,如果可执行文件嵌入了包含多个尺寸的图标,则Windows似乎会选择一个比任务栏图标的实际尺寸大的尺寸,然后将其缩小(以100%的比例将32像素图标的尺寸调整为24,即使提供了24像素的图标,其他比例也是如此。)

通过仅将正确大小的图标作为资源加载并向窗口发送WM_SETICON消息,我已经解决了C ++ MFC应用程序的此问题,这在任务栏和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); 
Run Code Online (Sandbox Code Playgroud)

该方法似乎不适用于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);
Run Code Online (Sandbox Code Playgroud)

使用了正确的图标,但它看起来模糊,好像有些东西已将其调整为“预期”大小,然后重新调整为原来的大小一样。左边是它的显示方式,右边是图标文件的内容。

Java应用程序的图标与外观

因此,我的问题是:我可以在该Java应用程序中执行什么操作,以使Windows呈现在任务栏上提供的图标,而不会缩放它并模糊细节?

Ser*_*Ten 4

sun.awt.SunToolkitgetScaledIconImage()中确实有一个称为缩放函数,在设置图标时总是使用它。您必须绕过此函数才能获得无别名的图标。所以你需要的是方法的替代品。java.awt.Window.setIconImages()

customSetIconImages()提供了几个图标图像 Icon16x16.png、Icon24x24.png 等。这是一个在 Windows 10 任务栏中放置清晰的 24x24 像素图标的示例。

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

正如 @df778899 所说,在 sun.awt.windows.WWindowPeer 内部有四个私有本机方法,您可以调用它们来确定系统图标大小。您可以将这些方法返回的信息与您自己的版本结合起来,getScaledIconImage()该版本执行或不执行您希望的取消别名。

最后,请注意,这是一个非常肮脏的黑客行为,只是为了获得一个无别名的图标。我只在 Java 8 和 Windows 10 中进行了测试。而且它很可能在较新版本的 Java 中不起作用。