I'm using the "Add a JSeparator together with the ComboBoxItem-to-render on a JPanel"-ListCellRenderer approach to display separators in a JComboBox.
我正在使用“在JPanel上添加JSeparator和ComboBoxItem-to-render”-ListCellRenderer方法来在JComboBox中显示分隔符。
I noticed that the algorithm on MacOS to vertically center the selected item on the PopUp gets confused by the changed height of the JSeparator-ComboBoxItems.
我注意到MacOS上用于垂直居中PopUp上所选项目的算法会因JSeparator-ComboBoxItems的更改高度而混淆。
Is there a way to fix the wrong position of the PopUps seen on the right-hand side of this screenshot? If the "Spain"-Item is selected it is painted slightly too high; the "Cars"-Item much too high.
有没有办法解决这个截图右侧看到的PopUps的错误位置?如果选择“西班牙” - 项目,它会被涂得略高; “汽车” - 太高了。
The sourcecode:
源代码:
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;
}
}
2 个解决方案
#1
2
This looks like a feature of com.apple.laf.AquaComboBoxUI
that tries to expose more of the list surrounding the current selection. It just doesn't expect the list to include a a JSeparator
having a com.apple.laf.AquaPopupMenuSeparatorUI
.
这看起来像com.apple.laf.AquaComboBoxUI的一个功能,它试图公开更多当前选择周围的列表。它只是不希望列表包含一个具有com.apple.laf.AquaPopupMenuSeparatorUI的JSeparator。
As an alternative, consider one of these approaches:
作为替代方案,请考虑以下方法之一:
-
Use HTML to decorate the entry, e.g.
使用HTML来装饰条目,例如
new JComboBox("<html><b>Spain</b></html>,Italy,Car,Peru"…
-
Alter the
Font
in the renderer, as shown here.更改渲染器中的Font,如此处所示。
#2
1
-
I think that this issue has nothing with
Platoform
/Native OS
/Look and Feel
我认为这个问题与Platoform / Native OS /外观无关
-
JComboBox
,JPopup
could / couldn't be restricted, changed, overrode some of methods in some Swing GUI BuildersJComboBox,JPopup可以/不能被限制,改变,覆盖一些Swing GUI Builders中的一些方法
-
for
JComboBoxes JPopup
to use Combo Box Popup by@camickr
对于JComboBoxes,JPopup使用@camickr的Combo Box Popup
-
maybe
JSeparator
inRenderer
could be little bit different也许Renderer中的JSeparator可能会有点不同
a) for possible output from
ActionListener
orItemListener
a)可能来自ActionListener或ItemListener的输出
b) notice this couldn't works for proper
KeyListener
added to the derivedJList
(maybe not important)b)注意这不适用于添加到派生JList的正确KeyListener(可能不重要)
images
图片
code
码
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;
}
}
}
}
#1
2
This looks like a feature of com.apple.laf.AquaComboBoxUI
that tries to expose more of the list surrounding the current selection. It just doesn't expect the list to include a a JSeparator
having a com.apple.laf.AquaPopupMenuSeparatorUI
.
这看起来像com.apple.laf.AquaComboBoxUI的一个功能,它试图公开更多当前选择周围的列表。它只是不希望列表包含一个具有com.apple.laf.AquaPopupMenuSeparatorUI的JSeparator。
As an alternative, consider one of these approaches:
作为替代方案,请考虑以下方法之一:
-
Use HTML to decorate the entry, e.g.
使用HTML来装饰条目,例如
new JComboBox("<html><b>Spain</b></html>,Italy,Car,Peru"…
-
Alter the
Font
in the renderer, as shown here.更改渲染器中的Font,如此处所示。
#2
1
-
I think that this issue has nothing with
Platoform
/Native OS
/Look and Feel
我认为这个问题与Platoform / Native OS /外观无关
-
JComboBox
,JPopup
could / couldn't be restricted, changed, overrode some of methods in some Swing GUI BuildersJComboBox,JPopup可以/不能被限制,改变,覆盖一些Swing GUI Builders中的一些方法
-
for
JComboBoxes JPopup
to use Combo Box Popup by@camickr
对于JComboBoxes,JPopup使用@camickr的Combo Box Popup
-
maybe
JSeparator
inRenderer
could be little bit different也许Renderer中的JSeparator可能会有点不同
a) for possible output from
ActionListener
orItemListener
a)可能来自ActionListener或ItemListener的输出
b) notice this couldn't works for proper
KeyListener
added to the derivedJList
(maybe not important)b)注意这不适用于添加到派生JList的正确KeyListener(可能不重要)
images
图片
code
码
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;
}
}
}
}