Значок JOptionPane обрезается в Windows 10

Я использую следующий код, чтобы представить диалог ошибки в Java Swing:

JOptionPane.showMessageDialog(null, "Arquivo de imagem não encontrado. Por gentileza, altere o caminho do arquivo.", "Erro",  JOptionPane.ERROR_MESSAGE);

используя Windows 10 по умолчанию, посмотрите через

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

Но значок выглядит обрезанным, вот так:

Диалог с обрезанным значком

Есть идеи, как это решить?

Вот SSCCE:

import javax.swing.JOptionPane;
import javax.swing.UIManager;

public class SSCCE {
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            JOptionPane.showMessageDialog(null, "Error message", "Error",  JOptionPane.ERROR_MESSAGE);
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}

1 ответ

Похоже, что Oracle, наконец, обратил на это внимание и исправит это в JDK 9, но на данный момент это решение для qiuck, hacky и Windows-зависимых для тех, кто не хочет предоставлять пользовательские значки.

Вставьте этот код после вашего звонка UIManager.setLookAndFeel():

try
{
    String[][] icons =
    {
        {"OptionPane.errorIcon", "65581"},
        {"OptionPane.warningIcon", "65577"},
        {"OptionPane.questionIcon", "65579"},
        {"OptionPane.informationIcon", "65583"}
    };
    //obtain a method for creating proper icons
    Method getIconBits = Class.forName("sun.awt.shell.Win32ShellFolder2").getDeclaredMethod("getIconBits", new Class[]{long.class, int.class});
    getIconBits.setAccessible(true);
    //calculate scaling factor
    double dpiScalingFactor = Toolkit.getDefaultToolkit().getScreenResolution() / 96.0;
    int icon32Size = (dpiScalingFactor == 1)?(32):((dpiScalingFactor == 1.25)?(40):((dpiScalingFactor == 1.5)?(45):((int) (32 * dpiScalingFactor))));
    Object[] arguments = {null, icon32Size};
    for (String[] s:icons)
    {
        if (UIManager.get(s[0]) instanceof ImageIcon)
        {
            arguments[0] = Long.valueOf(s[1]);
            //this method is static, so the first argument can be null
            int[] iconBits = (int[]) getIconBits.invoke(null, arguments);
            if (iconBits != null)
            {
                //create an image from the obtained array
                BufferedImage img = new BufferedImage(icon32Size, icon32Size, BufferedImage.TYPE_INT_ARGB);
                img.setRGB(0, 0, icon32Size, icon32Size, iconBits, 0, icon32Size);
                ImageIcon newIcon = new ImageIcon(img);
                //override previous icon with the new one
                UIManager.put(s[0], newIcon);
            }
        }
    }
}
catch (Exception e)
{
    e.printStackTrace();
}

Почему это работает

В Windows значки, предоставляемые приложениям, уже масштабируются в соответствии с текущими настройками DPI (96 = без масштабирования, 120 = +25%, 144 = +50%). К сожалению, Java всегда предполагает, что значки имеют размер 16x16 или 32x32, что не так. Приведенный выше код использует нативный метод sun.awt.shell.Win32ShellFolder2.getIconBits() используется Java для получения значков ОС, но предоставляет соответствующие размеры. Этот подход может стать непригодным в будущем, так как Win32ShellFolder2 не является частью API и может быть полностью изменен или удален, но пока это единственный подход, позволяющий сохранить собственные значки.

Ответ mkJ720 работает хорошо, но идентификаторы значков отключены. Вот исправленные:

String[][] icons = {
    {"OptionPane.warningIcon",     "65581"},
    {"OptionPane.questionIcon",    "65583"},
    {"OptionPane.errorIcon",       "65585"},
    {"OptionPane.informationIcon", "65587"}
};
Другие вопросы по тегам