Reputation: 545518
I want to modify the display of a (non-editable) JComboBox
in such a fashion that the currently selected entry has some extra text in the edit field (not the dropdown list, though).
Something like this:
My first guess was to override the ComboBox’ model so that getSelectedItem
returns a wrapper object modifying the display:
petList.setModel(new ComboBoxModel() {
private Object selected;
public void setSelectedItem(Object anItem) {
selected = anItem;
}
public Object getSelectedItem() {
return new ActiveComboItem(selected);
}
// … The rest of the methods are straightforward.
});
Where ActiveComboItem
looks as follows:
static class ActiveComboItem {
private final Object item;
public ActiveComboItem(Object item) { this.item = item; }
@Override
public boolean equals(Object other) {
return item == null ? other == null : item.equals(other);
}
@Override
public String toString() { return String.format("Animal: %s", item); }
}
Indeed, this works as far as modifying the display goes. Unfortunately, the current entry is no longer marked as active:
(Note the missing check mark … or however the selection is displayed by your OS.)
Further inspection shows that the model’s getElementAt
method is called with an index of -1
every time the user selects a new item in the box. This is only the case when using the modified selected item. When the model’s getSelectedItem
method returns the plain object without wrapper, then the selected item is marked as selected in the dropdown box, and getElementAt
is not called with an argument of -1
.
Apparently, the ComboBox is comparing each item in turn to the currently active item but, despite my overriding the equals
method, it finds no match. How can I fix this?
(Full, compilable code for this problem at gist.github.com)
Upvotes: 4
Views: 5739
Reputation: 109815
create JTextField
and JButton
with Icon
, for JButton
implements ButtonModel
, by overriding its methods isRollover(), isPressed(), isArmed()
don't extract Icon
from JComboBox
, paint own Triangle, then JButton
with Icon
will be Look and Feel
and Native OS
resist, for nicer output implements JButton#setRolloverIcon()
too
create a JPopup
or JWindow
, put here JScrollPane
, display this Container from model.isPressed()
or isArmed
now you have two choises
1) create JList
that contains JCheckBox
(remove rectangle from JCheckBox)
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
public class CheckList {
public static void main(String args[]) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a list containing CheckListItem's
JList list = new JList(new CheckListItem[]{
new CheckListItem("apple"),
new CheckListItem("orange"),
new CheckListItem("mango"),
new CheckListItem("paw paw"),
new CheckListItem("banana")});
// Use a CheckListRenderer (see below) to renderer list cells
list.setCellRenderer(new CheckListRenderer());
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addMouseListener(new MouseAdapter() {// Add a mouse listener to handle changing selection
@Override
public void mouseClicked(MouseEvent event) {
JList list = (JList) event.getSource();
int index = list.locationToIndex(event.getPoint());// Get index of item clicked
CheckListItem item = (CheckListItem) list.getModel().getElementAt(index);
item.setSelected(!item.isSelected()); // Toggle selected state
list.repaint(list.getCellBounds(index, index));// Repaint cell
}
});
frame.getContentPane().add(new JScrollPane(list));
frame.pack();
frame.setVisible(true);
}
}
// Represents items in the list that can be selected
class CheckListItem {
private String label;
private boolean isSelected = false;
public CheckListItem(String label) {
this.label = label;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
@Override
public String toString() {
return label;
}
}
// Handles rendering cells in the list using a check box
class CheckListRenderer extends JCheckBox implements ListCellRenderer {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {
setEnabled(list.isEnabled());
setSelected(((CheckListItem) value).isSelected());
setFont(list.getFont());
setBackground(list.getBackground());
setForeground(list.getForeground());
setText(value.toString());
return this;
}
}
.
2) or implements ListSelectionModel and add/remove Icon
on MouseClick
hide JPopup
or JWindow
from MouseListener
Event
Upvotes: -1
Reputation: 40811
You need to provide a custom ListCellRenderer. The following works:
final JComboBox animalCombo = new JComboBox(animals);
animalCombo.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(final JList list, Object value, final int index, final boolean isSelected,
final boolean cellHasFocus) {
if (index == -1) {
value = "Animal: " + value;
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
});
index is -1 when the value its painting is the one that's not in the dropdown.
For future reference, when you just want to change how something is displayed in Swing, you never want to modify the backing model. Every component has a renderer, and generally you just need to slightly modify the default one.
Upvotes: 8