4

我正在使用“在 JPanel 上添加 JSeparator 和 ComboBoxItem-to-render” -ListCellRenderer 方法在 JComboBox 中显示分隔符。

我注意到 MacOS 上将 PopUp 上的选定项目垂直居中的算法被 JSeparator-ComboBoxItems 的高度变化弄糊涂了。

有没有办法修复此屏幕截图右侧看到的弹出窗口的错误位置?如果选择了“西班牙”-项目,它被画得有点太高了;“汽车”-项目太高了。

JComboBox 分隔符 MacOs 错误

源代码:

import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Arrays;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.ListCellRenderer;

public class JComboBoxSeparatorMacOs {

    public static void main(String[] args) {
    JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new JComboBox<String>("A,Normal,Combo Box,without Separators".split(",")), BorderLayout.WEST);

        JComboBox<String> comboBox = new JComboBox<String>("Spain,Italy,Car,Peru".split(","));
        ListCellRenderer<String> renderer = new SeparatorListCellRenderer<String>(comboBox.getRenderer(), 0);
        comboBox.setRenderer(renderer);
        frame.add(comboBox);

        frame.pack();
        frame.setVisible(true);
    }
}

class SeparatorListCellRenderer<E> implements ListCellRenderer<E> {
    private final ListCellRenderer<? super E> delegate;
    private final int[] indexes;
    private final JPanel panel = new JPanel(new BorderLayout());

    public SeparatorListCellRenderer(ListCellRenderer<? super E> delegate, int... indexes) {
        Arrays.sort(indexes);
        this.delegate = delegate;
        this.indexes = indexes;
        panel.setOpaque(false);  //for rendering of selected item on MSWindows
    }

    @Override
    public Component getListCellRendererComponent(JList list, E value, int index, boolean isSelected, boolean cellHasFocus) {
        panel.removeAll();
        panel.add(delegate.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus));
        if (Arrays.binarySearch(indexes, index) >= 0)
            panel.add(new JSeparator(), BorderLayout.PAGE_END);

        return panel;
    }
}
4

2 回答 2

2

这看起来像是一个com.apple.laf.AquaComboBoxUI试图公开更多围绕当前选择的列表的功能。它只是不希望列表包含JSeparator具有com.apple.laf.AquaPopupMenuSeparatorUI.

作为替代方案,请考虑以下方法之一:

  • 使用 HTML 来装饰条目,例如

    new JComboBox("<html><b>Spain</b></html>,Italy,Car,Peru"…
    
  • 更改渲染器中的 ,如此Font所示。

于 2012-10-09T16:55:03.467 回答
1
  • 我认为这个问题与//Platoform无关Native OSLook and Feel

  • JComboBox,JPopup可以/不能被限制、改变、覆盖一些 Swing GUI Builders 中的一些方法

  • 用于JComboBoxes JPopup使用组合框弹出窗口@camickr

  • 也许JSeparatorRenderer可能有点不同

    a) 可能的输出ActionListenerItemListener

    b)请注意,这不适用于正确KeyListener添加到派生JList(可能不重要)

图片

在此处输入图像描述在此处输入图像描述

代码

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class ComboBoxWithSeparator extends JFrame {

    private static final long serialVersionUID = 1L;
    final String SEPARATOR = "SEPARATOR";

    public ComboBoxWithSeparator() {
        super("Block ComboBox Example");
        String[][] str = {{"A", "B", "C"}, {"1", "2", "3"}, {"abc", "def", "ghi"}};
        JComboBox combo = new JComboBox(makeVectorData(str));
        combo.setRenderer(new ComboBoxRenderer());
        combo.addActionListener(new BlockComboListener(combo));
        combo.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXX");
        setLayout(new FlowLayout());
        JComboBox combo1 = new JComboBox(makeVectorData(str));
        combo1.setRenderer(new ComboBoxRenderer());
        combo1.addActionListener(new BlockComboListener(combo));
        combo1.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXX");
        BoundsPopupMenuListener listener = new BoundsPopupMenuListener(true, true);
        combo1.addPopupMenuListener(listener);
        combo1.setPrototypeDisplayValue("ItemWWW");
        add(combo);
        add(combo1);
        pack();
        setVisible(true);
    }

    private Vector<String> makeVectorData(String[][] str) {
        boolean needSeparator = false;
        Vector<String> data = new Vector<String>();
        for (int i = 0; i < str.length; i++) {
            if (needSeparator) {
                data.addElement(SEPARATOR);
            }
            for (int j = 0; j < str[i].length; j++) {
                data.addElement(str[i][j]);
                needSeparator = true;
            }
        }
        return data;
    }

    public static void main(String args[]) {
        try {
            for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(laf.getName())) {
                    UIManager.setLookAndFeel(laf.getClassName());
                    //UIManager.getLookAndFeelDefaults().put("Panel.background", Color.white);
                    //UIManager.getLookAndFeelDefaults().put("Button.contentMargins", new InsetsUIResource(0,0,0,0));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        ComboBoxWithSeparator frame = new ComboBoxWithSeparator();
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    private class ComboBoxRenderer extends JLabel implements ListCellRenderer {

        private static final long serialVersionUID = 1L;
        private JSeparator separator;

        public ComboBoxRenderer() {
            setOpaque(true);
            setBorder(new EmptyBorder(1, 1, 1, 1));
            separator = new JSeparator(JSeparator.HORIZONTAL);
        }

        @Override
        public Component getListCellRendererComponent(JList list, Object value,
                int index, boolean isSelected, boolean cellHasFocus) {
            String str = (value == null) ? "" : value.toString();
            if (SEPARATOR.equals(str)) {
                return separator;
            }
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            setFont(list.getFont());
            setText(str);
            return this;
        }
    }

    private class BlockComboListener implements ActionListener {

        private JComboBox combo;
        private Object currentItem;

        BlockComboListener(JComboBox combo) {
            this.combo = combo;
            combo.setSelectedIndex(0);
            currentItem = combo.getSelectedItem();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            String tempItem = (String) combo.getSelectedItem();
            if (SEPARATOR.equals(tempItem)) {
                combo.setSelectedItem(currentItem);
            } else {
                currentItem = tempItem;
            }
        }
    }
}
于 2012-10-09T11:43:54.470 回答