Java Swing - JList Filtering and Highlighting Example

[Updated: Nov 17, 2017, Created: Nov 17, 2017]

This example shows how to use JTextField to filter JList items and at the same time highlight the matching text. To achieve highlighting, we will extends JLabel and override its paintComponent() method to fill 2d rectangles at the matched text locations.

Extending JLabel

package com.logicbig.example;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

public class LabelHighlighted extends JLabel {
  private List<Rectangle2D> rectangles = new ArrayList<>();
  private Color colorHighlight = Color.YELLOW;

  public void reset() {
      rectangles.clear();
      repaint();
  }

  public void highlightText(String textToHighlight) {
      if (textToHighlight == null) {
          return;
      }
      reset();

      final String textToMatch = textToHighlight.toLowerCase().trim();
      if (textToMatch.length() == 0) {
          return;
      }
      textToHighlight = textToHighlight.trim();

      final String labelText = getText().toLowerCase();
      if (labelText.contains(textToMatch)) {
          FontMetrics fm = getFontMetrics(getFont());
          float w = -1;
          final float h = fm.getHeight() - 1;
          int i = 0;
          while (true) {
              i = labelText.indexOf(textToMatch, i);
              if (i == -1) {
                  break;
              }
              if (w == -1) {
                  String matchingText = getText().substring(i,
                          i + textToHighlight.length());
                  w = fm.stringWidth(matchingText);
              }
              String preText = getText().substring(0, i);
              float x = fm.stringWidth(preText);
              rectangles.add(new Rectangle2D.Float(x, 1, w, h));
              i = i + textToMatch.length();
          }
          repaint();
      }
  }

  @Override
  protected void paintComponent(Graphics g) {
      if (rectangles.size() > 0) {
          Graphics2D g2d = (Graphics2D) g;
          Color c = g2d.getColor();
          for (Rectangle2D rectangle : rectangles) {
              g2d.setColor(colorHighlight);
              g2d.fill(rectangle);
              g2d.setColor(Color.LIGHT_GRAY);
              g2d.draw(rectangle);
          }
          g2d.setColor(c);
      }
      super.paintComponent(g);

  }
}

The main class

public class JListHighlightedFilterExample {
  public static void main(String[] args) {
      List<Employee> employees = EmployeeDataAccess.getEmployees();
      DefaultListModel<Employee> model = new DefaultListModel<>();
      employees.forEach(model::addElement);
      JList<Employee> jList = new JList<>(model);
      JListFilterDecorator decorator = JListFilterDecorator
              .decorate(jList, JListHighlightedFilterExample::employeeFilter);
      jList.setCellRenderer(createListRenderer(decorator.getFilterField()));

      JFrame frame = createFrame();
      frame.add(decorator.getContentPanel());
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
  }

  private static boolean employeeFilter(Employee emp, String str) {
      return getEmployeeDisplayText(emp).toLowerCase().contains(str.toLowerCase());
  }

  private static String getEmployeeDisplayText(Employee emp){
      return String.format("%s [%s]", emp.getName(), emp.getDept());
  }

  private static ListCellRenderer<? super Employee> createListRenderer(JTextField filterField) {
      return new DefaultListCellRenderer() {
          private Color background = new Color(0, 100, 255, 15);
          private Color defaultBackground = (Color) UIManager.get("List.background");

          @Override
          public Component getListCellRendererComponent(JList<?> list, Object value, int index,
                                                        boolean isSelected, boolean cellHasFocus) {
              Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

              if (c instanceof JLabel) {
                  Employee emp = (Employee) value;
                  JLabel original = (JLabel) c;
                  LabelHighlighted label = new LabelHighlighted();
                  label.setText(getEmployeeDisplayText(emp));
                  label.setFont(original.getFont().deriveFont(14f).deriveFont(Font.PLAIN));
                  label.setBackground(original.getBackground());
                  label.setForeground(original.getForeground());
                  label.setHorizontalTextPosition(original.getHorizontalTextPosition());
                  label.highlightText(filterField.getText());
                  return label;
              }
              return c;
          }
      };
  }

  private static JFrame createFrame() {
      JFrame frame = new JFrame("JList Example");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(new Dimension(600, 300));
      return frame;
  }
}

Decorator for JList filtering

public class JListFilterDecorator {
 private JPanel contentPanel;
 private JTextField filterField;

  public static <T> JListFilterDecorator decorate(JList<T> jList, BiPredicate<T, String> userFilter) {
      if (!(jList.getModel() instanceof DefaultListModel)) {
          throw new IllegalArgumentException("List model must be an instance of DefaultListModel");
      }
      DefaultListModel<T> model = (DefaultListModel<T>) jList.getModel();
      List<T> items = getItems(model);
      JTextField textField = new JTextField();
      textField.getDocument().addDocumentListener(new DocumentListener() {
          @Override
          public void insertUpdate(DocumentEvent e) {
              filter();
          }

          @Override
          public void removeUpdate(DocumentEvent e) {
              filter();
          }

          @Override
          public void changedUpdate(DocumentEvent e) {
              filter();
          }

          private void filter() {
              model.clear();
              String s = textField.getText();
              for (T item : items) {
                  if(userFilter.test(item, s)){
                      model.addElement(item);
                  }
              }
          }
      });

      JPanel panel = new JPanel(new BorderLayout());
      panel.add(textField, BorderLayout.NORTH);
      JScrollPane pane = new JScrollPane(jList);
      panel.add(pane);


      JListFilterDecorator decorator = new JListFilterDecorator();
      decorator.contentPanel =panel;
      decorator.filterField = textField;
      return decorator;
  }

  private static <T> List<T> getItems(DefaultListModel<T> model) {
      List<T> list = new ArrayList<>();
      for (int i = 0; i < model.size(); i++) {
          list.add(model.elementAt(i));
      }
      return list;
  }

  public JTextField getFilterField() {
      return filterField;
  }

  public JPanel getContentPanel() {
      return contentPanel;
  }
}
public class Employee {
  private String name;
  private String dept;
  private String phone;
  private String address;
    .............
}

Output

Example Project

Dependencies and Technologies Used :

  • datafactory 0.8: Library to generate data for testing.
  • JDK 1.8
  • Maven 3.3.9

JList Filter with Highlighting Example Select All Download
  • list-filter-highlighting
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also