Bootstrap

实现JList过滤与高亮显示的完整教程

在Java Swing开发中,JList是一个常用的组件,用于显示一组数据。然而,有时我们希望在用户输入时动态过滤列表项,并对匹配的文本进行高亮显示。本文将通过一个完整的示例,展示如何实现这一功能。
一、核心思路
为了实现JList的过滤和高亮显示,我们需要完成以下步骤:
扩展JLabel以支持高亮显示:通过重写paintComponent方法,在匹配的文本位置绘制高亮区域。
实现过滤逻辑:监听文本框的输入变化,动态更新JList的显示内容。
自定义ListCellRenderer:将扩展的JLabel作为列表单元的渲染器,实现高亮显示。
二、代码实现

  1. 扩展JLabel实现高亮功能
    我们首先创建一个扩展自JLabel的类LabelHighlighted,用于支持文本高亮显示。
    java复制
    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 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 (isOpaque()) {
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());
    }
    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);
}

}
2. 创建过滤装饰器JListFilterDecorator
接下来,我们实现一个装饰器类JListFilterDecorator,用于监听文本框输入并动态过滤JList的内容。
java复制
package com.logicbig.example;

import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiPredicate;

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;
}

}
3. 主类实现
最后,我们通过主类JListHighlightedFilterExample将上述功能整合起来。
java复制
package com.logicbig.example;

import javax.swing.*;
import java.util.List;

public class JListHighlightedFilterExample {
public static void main(String[] args) {
List employees = EmployeeDataAccess.getEmployees();
DefaultListModel model = new DefaultListModel<>();
employees.forEach(model::addElement);
JList 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.setOpaque(original.isOpaque());
                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;
}

}
4. 数据模型类Employee
为了完整起见,我们还需要一个简单的Employee类作为数据模型。
java复制
package com.logicbig.example;

public class Employee {
private String name;
private String dept;
private String phone;
private String address;

public Employee(String name, String dept, String phone, String address) {
    this.name = name;
    this.dept = dept;
    this.phone = phone;
    this.address = address;
}

public String getName() {
    return name;
}

public String getDept() {
    return dept;
}

public String getPhone() {
    return phone;
}

public String getAddress() {
    return address;
}

}
三、运行效果
运行程序后,你将看到一个包含员工信息的列表。在文本框中输入内容时,列表会自动过滤并高亮显示匹配的文本。
四、总结
通过扩展JLabel和实现过滤逻辑,我们成功地为JList添加了过滤和高亮显示功能。这种实现方式不仅功能强大,而且具有很高的灵活性,可以轻松扩展到其他类似的场景。希望本文能为你在Java Swing开发中提供一些帮助。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;