In the last example, we implemented filtering functionality for JComboBox, there we had to make JComboBox editable, but in fact the purpose was not editing but filtering. Also it might not be always possible, for any reasons, to enable editing flag for the JComboBox. In following example, we will implement filtering without making the combo box editable. We are going to create a custom popup component to show the filtering text. This will also preserve the default functionality of the JComboBox of showing selection changes in the main text component while navigating the drop down list.
Main class
public class JComboBoxFilterExample {
public static void main(String[] args) {
List<Employee> employees = EmployeeDataAccess.getEmployees();
JComboBox<Employee> comboBox = new JComboBox<>(
employees.toArray(new Employee[employees.size()]));
ComboBoxFilterDecorator<Employee> decorate = ComboBoxFilterDecorator.decorate(comboBox,
JComboBoxFilterExample::employeeFilter);
comboBox.setRenderer(new CustomComboRenderer(decorate.getFilterLabel()));
JPanel panel = new JPanel();
panel.add(comboBox);
JFrame frame = createFrame();
frame.add(panel);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
.............
}
The Filtering decorator
public class ComboBoxFilterDecorator<T> {
private Popup filterPopup;
private JLabel filterLabel;
private JComboBox<T> comboBox;
private BiPredicate<T, String> userFilter;
java.util.List<T> items;
private TextHandler textHandler = new TextHandler();
private Object selectedItem;
public ComboBoxFilterDecorator(JComboBox<T> comboBox, BiPredicate<T, String> userFilter) {
this.comboBox = comboBox;
this.userFilter = userFilter;
}
public static <T> ComboBoxFilterDecorator<T> decorate(JComboBox<T> comboBox,
BiPredicate<T, String> userFilter) {
ComboBoxFilterDecorator decorator = new ComboBoxFilterDecorator(comboBox, userFilter);
decorator.init();
return decorator;
}
private void init() {
prepareComboFiltering();
initFilterLabel();
initComboPopupListener();
initComboKeyListener();
}
private void prepareComboFiltering() {
DefaultComboBoxModel<T> model = (DefaultComboBoxModel<T>) comboBox.getModel();
items = new ArrayList<>();
for (int i = 0; i < model.getSize(); i++) {
items.add(model.getElementAt(i));
}
}
private void initComboKeyListener() {
comboBox.addKeyListener(
new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
char keyChar = e.getKeyChar();
if (!Character.isDefined(keyChar)) {
return;
}
int keyCode = e.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_DELETE:
return;
case KeyEvent.VK_ENTER:
resetFilterPopup();
return;
case KeyEvent.VK_ESCAPE:
if (selectedItem != null) {
comboBox.setSelectedItem(selectedItem);
}
resetFilterPopup();
return;
case KeyEvent.VK_BACK_SPACE:
textHandler.removeCharAtEnd();
break;
default:
textHandler.add(keyChar);
}
if (!comboBox.isPopupVisible()) {
comboBox.showPopup();
}
if (!textHandler.text.isEmpty()) {
showFilterPopup();
performFilter();
} else {
resetFilterPopup();
}
e.consume();
}
}
);
}
private void initFilterLabel() {
filterLabel = new JLabel();
filterLabel.setOpaque(true);
filterLabel.setBackground(new Color(255, 248, 220));
filterLabel.setFont(filterLabel.getFont().deriveFont(Font.PLAIN));
filterLabel.setBorder(BorderFactory.createLineBorder(Color.gray));
}
public JLabel getFilterLabel() {
return filterLabel;
}
private void initComboPopupListener() {
comboBox.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
resetFilterPopup();
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
resetFilterPopup();
}
});
}
private void showFilterPopup() {
if (textHandler.getText().isEmpty()) {
return;
}
if (filterPopup == null) {
Point p = new Point(0, 0);
SwingUtilities.convertPointToScreen(p, comboBox);
Dimension comboSize = comboBox.getPreferredSize();
filterLabel.setPreferredSize(new Dimension(comboSize));
filterPopup = PopupFactory.getSharedInstance().getPopup(comboBox, filterLabel, p.x,
p.y - filterLabel.getPreferredSize().height);
selectedItem = comboBox.getSelectedItem();
}
filterPopup.show();
}
private void resetFilterPopup() {
if (!textHandler.isEditing()) {
return;
}
if (filterPopup != null) {
filterPopup.hide();
filterPopup = null;
filterLabel.setText("");
textHandler.reset();
//add items in the original order
Object selectedItem = comboBox.getSelectedItem();
DefaultComboBoxModel<T> model = (DefaultComboBoxModel<T>) comboBox.getModel();
model.removeAllElements();
for (T item : items) {
model.addElement(item);
}
//preserve the selection
model.setSelectedItem(selectedItem);
this.selectedItem = selectedItem;
}
}
private void performFilter() {
filterLabel.setText(textHandler.getText());
// System.out.println("'" + textHandler.getText() + "'");
DefaultComboBoxModel<T> model = (DefaultComboBoxModel<T>) comboBox.getModel();
model.removeAllElements();
java.util.List<T> filteredItems = new ArrayList<>();
//add matched items first
for (T item : items) {
if (userFilter.test(item, textHandler.getText())) {
model.addElement(item);
} else {
filteredItems.add(item);
}
}
if (model.getSize() == 0) {
//if no match then red font
filterLabel.setForeground(Color.red);
} else {
filterLabel.setForeground(Color.blue);
}
//add unmatched items
filteredItems.forEach(model::addElement);
}
private static class TextHandler {
private String text = "";
private boolean editing;
public void add(char c) {
text += c;
editing = true;
}
public void removeCharAtEnd() {
if (text.length() > 0) {
text = text.substring(0, text.length() - 1);
editing = true;
}
}
public void reset() {
text = "";
editing = false;
}
public String getText() {
return text;
}
public boolean isEditing() {
return editing;
}
}
}
Output
Example ProjectDependencies and Technologies Used: - datafactory 0.8: Library to generate data for testing.
- JDK 1.8
- Maven 3.3.9
|
|