JOptionPane 没有带有自定义 LNF 的按钮文本

JOptionPane no button text with custom LNF

我尝试设计一个定制的 Dialog 样式,它允许用户创建一个新文件夹。在我加载自定义外观之前,一切看起来都很好:

现在我尝试设置外观,但按钮的文本消失了。

我设置了一个 SSCCE 来演示这种行为。请注意,LNF class 除了从 UIManager.getDefaults() 加载所有值外什么都不做。如果这有帮助,我将使用 Debian 8 (jessie) 和 Gnome 3.18.1 并打开全局深色主题。

为什么会发生这种情况,如何解决这些按钮文本消失的问题?

UIManager.getDefaults() 检索到的值不包括 JOptionPane 按钮的文本,其属性分别为 OptionPane.cancelButtonTextOptionPane.okButtonText。这会导致按钮显示时没有任何文本,导致它们与问题中提供的屏幕截图一样小。通过将 OptionPane.cancelButtonTextOptionPane.okButtonText 设置为所需的值,JOptionPane 按钮将显示设置的文本。此解决方案也适用于 yes/no 按钮。

另一种常用的方法是使用UIDefaults#addResourceBundle(...)方法:

//@see javax/swing/plaf/basic/BasicLookAndFeel.java
@Override
public UIDefaults getDefaults() {
  UIDefaults defaults = new UIDefaults();
  defaults.putAll(UIManager.getDefaults());
  //defaults.addResourceBundle("com.example.swing.plaf.light.resources.light");
  defaults.addResourceBundle("com.sun.swing.internal.plaf.basic.resources.basic");
  return defaults;
}

Test2.java

import java.awt.*;
import java.io.File;
import java.util.*;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.GroupLayout.SequentialGroup;

public class Test2 {
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(new LightLookAndFeel());
            DialogFactory.showCreateFolderDialog(null, new File("."));
        } catch (UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
    }
}

class DialogFactory {
    private DialogFactory() {}

    public static void showCreateFolderDialog(Component component, File parent) {
        JPanel panel = new JPanel();
        JLabel message = new JLabel("Please enter a folder name.");
        JLabel label = new JLabel("Folder name:");
        final JLabel errorMessage = new JLabel();
        JTextField folderName = new JTextField();
        GroupLayout layout = new GroupLayout(panel);
        layout.setHonorsVisibility(false);
        layout.setAutoCreateContainerGaps(true);
        layout.setAutoCreateGaps(true);
        panel.setLayout(layout);
        errorMessage.setVisible(false);

        SequentialGroup hGroup = layout.createSequentialGroup();
        SequentialGroup input = layout.createSequentialGroup().addComponent(label).addComponent(folderName);
        hGroup.addGroup(layout.createParallelGroup().addComponent(message).addGroup(input).addComponent(errorMessage));
        layout.setHorizontalGroup(hGroup);

        SequentialGroup vGroup = layout.createSequentialGroup();
        vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(message));
        vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(label).addComponent(folderName));
        vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).addComponent(errorMessage));
        layout.setVerticalGroup(vGroup);

        final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
        final JDialog dialog = new JDialog(JOptionPane.getFrameForComponent(component), "Create new folder", true);
        dialog.setLocationRelativeTo(component);
        dialog.setContentPane(optionPane);
        dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        optionPane.addPropertyChangeListener(event -> {
            String property = event.getPropertyName();

            if (dialog.isVisible() && event.getSource() == optionPane && property.equals(JOptionPane.VALUE_PROPERTY)) {
                if (folderName.getText().isEmpty() && (int) event.getNewValue() != JOptionPane.CANCEL_OPTION) {
                    errorMessage.setText("<html><body><b style='color: red'>Please enter a valid folder name!</b></body></html>");
                    errorMessage.setVisible(true);
                    dialog.pack();
                } else {
                    dialog.setVisible(false);
                }
            }
        });
        dialog.pack();
        dialog.setVisible(true);

        int response = ((Integer) optionPane.getValue()).intValue();
        if (response != JOptionPane.OK_OPTION) return;
        File newFolder = new File(parent, folderName.getText());
        newFolder.mkdirs();
    }
}

class LightLookAndFeel extends LookAndFeel {
    private final UIDefaults defaults = new UIDefaults();

    public LightLookAndFeel() {
        defaults.putAll(UIManager.getDefaults());
    }

    @Override
    public void initialize() {
    }

    @Override
    public String getName() {
        return "Light";
    }

    @Override
    public String getID() {
        return getClass().getName();
    }

    @Override
    public String getDescription() {
        return "Light Look and Feel";
    }

    @Override
    public boolean isNativeLookAndFeel() {
        return false;
    }

    @Override
    public boolean isSupportedLookAndFeel() {
        return true;
    }

    @Override
    public UIDefaults getDefaults() {
        //defaults.addResourceBundle("com.example.swing.plaf.light.resources.light");
        defaults.addResourceBundle("com.sun.swing.internal.plaf.basic.resources.basic");
        return defaults;
    }
}

// package com.example.swing.plaf.light.resources;
// import java.util.*;
//
// public class light extends ListResourceBundle {
//     @Override protected final Object[][] getContents() {
//         return new Object[][] {
//             //...
//             { "OptionPane.cancelButtonMnemonic", "0" },
//             { "OptionPane.cancelButtonText", "Cancel" },
//             { "OptionPane.inputDialogTitle", "Input" },
//             { "OptionPane.messageDialogTitle", "Message" },
//             { "OptionPane.noButtonMnemonic", "78" },
//             { "OptionPane.noButtonText", "No" },
//             { "OptionPane.okButtonMnemonic", "0" },
//             { "OptionPane.okButtonText", "OKkkk" },
//             { "OptionPane.titleText", "Select an Option" },
//             { "OptionPane.yesButtonMnemonic", "89" },
//             { "OptionPane.yesButtonText", "Yessss" },
//             //...
//         };
//     }
// }
// public class light_de extends ListResourceBundle { //...
// public class light_es extends ListResourceBundle { //...
// public class light_fr extends ListResourceBundle { //...
// public class light_it extends ListResourceBundle { //...
// public class light_ja extends ListResourceBundle { //...
// public class light_ko extends ListResourceBundle { //...
// public class light_pt_BR extends ListResourceBundle { //...
// public class light_sv extends ListResourceBundle { //...
// public class light_zh_CN extends ListResourceBundle { //...
// public class light_zh_HK extends ListResourceBundle { //...
// public class light_zh_TW extends ListResourceBundle { //...