简体   繁体   中英

Can I get native-looking JPopupMenu appearance for Swing on Mac OS X?

I'm trying to make a combo box in Swing (under Java 7) look like a native combo box. It turns out that a JPopupMenu is used to display the options of the combo box, so it turns into a matter of making a JPopupMenu look native enough.

If I use the default Aqua look and feel, I get square edges, which is not right at all, so we'll throw that idea away right off the bat. So of course, one turns to Quaqua, which is supposed to fix this sort of thing.

public class PopupMenuTest implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new PopupMenuTest());
    }

    @Override
    public void run() {
        try {
            UIManager.setLookAndFeel(QuaquaManager.getLookAndFeel());
            // Uncomment this for the second example
            //PopupFactory.setSharedInstance(new CustomisedScreenPopupFactory());
        } catch (Exception ignore) {}

        JComboBox<String> comboBox = new JComboBox<>();
        comboBox.setModel(new DefaultComboBoxModel<>(
            new String[] { "One", "Two", "Three" }));

        JFrame frame = new JFrame("Test");
        frame.setLayout(new FlowLayout());
        frame.add(comboBox);
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

This gives me 弹出菜单,该菜单确实具有圆角,但用黑色填充了角落 .

Where the code is commented out above, I tried jamming in a custom screen popup factory.

public class CustomisedScreenPopupFactory extends PopupFactory {
    private final PopupFactory delegate;

    public CustomisedScreenPopupFactory() {
        PopupFactory delegate;
        try {
            Class<? extends PopupFactory> clazz =
                Class.forName("com.apple.laf.ScreenPopupFactory")
                    .asSubclass(PopupFactory.class);
            Constructor<? extends PopupFactory> constructor =
                clazz.getDeclaredConstructor();
            constructor.setAccessible(true); // hacks
            delegate = constructor.newInstance();
        } catch (Exception ignore) {
            delegate = new PopupFactory(); // has to be set to something
        }
        this.delegate = delegate;
    }

    @Override
    public Popup getPopup(Component owner, Component contents, int x, int y) {
        Popup popup = delegate.getPopup(owner, contents, x, y);

        try {
            Method method = Popup.class.getDeclaredMethod("getComponent");
            method.setAccessible(true);
            Component component = (Component) method.invoke(popup);
            if (component instanceof JWindow) {    // always is, so far
                JWindow window = (JWindow) component;
                JRootPane rootPane = window.getRootPane();

                // This call here is what all the rest of the boilerplate was
                // added in order to access.
                AWTUtilities.setWindowOpaque(window, false);
            }
        } catch (Exception e) {
            Logger.getLogger(getClass()).error("Couldn't customise the popup window", e);
        }

        return popup;
    }
}

This gives me 一个有趣的结果,黑角消失了,阴影也消失了 .

Problem is, I want both the rounded corners and the shadow. Is it possible to get both?

Incidentally, I notice that IDEA does get it right, but I couldn't figure out from their source why it would work, so I wonder if it's because it's running on Java 6 and not Java 7...

You can match the system's look and feel like this:

    try{
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName);
    catch(Exception e){}

that just gets the system L&F, which would be more original than an external L&F anyways.

hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM