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