JTable의 값을 검색할때 버튼없이 자동검색하려고 할 때 DocumentListener
와 TableRowSorter
를 쓰면 된다.
✔️ TableRowSorter<DefaultTableModel> sorter;
private void search() {
String searchText = searchField.getText().trim();
// 검색어가 없으면 모든 행을 다시 보여준다.
if (searchText.isEmpty()) {
sorter.setRowFilter(null);
} else {
try {
sorter.setRowFilter(RowFilter.regexFilter("(?i)" + searchText));
} catch (PatternSyntaxException ex) {
// 검색어가 정규식으로 변환되지 않으면 모든 행을 보여준다.
sorter.setRowFilter(null);
}
}
}
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import java.util.regex.PatternSyntaxException;
public class TableSearchExample extends JFrame {
private final JTextField searchField = new JTextField();
private final JTable table;
private final DefaultTableModel tableModel;
public TableSearchExample() {
setTitle("JTable Search Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 테이블 모델 초기화
tableModel = new DefaultTableModel();
tableModel.addColumn("이름");
tableModel.addColumn("나이");
tableModel.addRow(new Object[]{"홍길동", 30});
tableModel.addRow(new Object[]{"김철수", 25});
tableModel.addRow(new Object[]{"이영희", 27});
// 테이블 초기화
table = new JTable(tableModel);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
// 검색 필드 초기화
TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(tableModel);
table.setRowSorter(sorter);
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
search();
}
@Override
public void removeUpdate(DocumentEvent e) {
search();
}
@Override
public void changedUpdate(DocumentEvent e) {
search();
}
private void search() {
String searchText = searchField.getText().trim();
// 검색어가 없으면 모든 행을 보여준다
if (searchText.isEmpty()) {
sorter.setRowFilter(null);
return;
}
try {
sorter.setRowFilter(RowFilter.regexFilter("(?i)" + searchText));
} catch (PatternSyntaxException ex) {
System.err.println("검색어 오류: " + ex.getMessage());
sorter.setRowFilter(null);
}
}
});
JPanel searchPanel = new JPanel();
searchPanel.add(new JLabel("검색"));
searchPanel.add(searchField);
add(searchPanel, "South");
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
TableSearchExample example = new TableSearchExample();
example.setVisible(true);
}
}
위 예제 코드에서는 searchField에 DocumentListener
를 등록하여 검색어가 변경될 때마다 search()
메서드를 호출한다.
search()
메서드에서는 TableRowSorter
를 사용하여 검색어가 포함된 행만 보여주도록 RowFilter
를 설정한다. 그리고 검색어가 없으면 모든 행을 보여준다.
(?i)는 대소문자를 무시하도록 설정하는 정규식 표현이다.
PatternSyntaxException
은 검색어가 잘못된 정규식 형식일 때 발생한다. try-catch를 사용해 예외처리를 해준다.
이렇게 구현하면 검색어가 변경될 때마다 해당하는 행만 보여준다.
행을 선택할 때 콘솔창에 선택한 행의 정보가 잘 나오는지 찍어보자.
이전에 작성한 예제 코드에서 ListSelectionListener
를 추가하고, valueChanged()
메서드에서 선택한 행의 정보를 출력하도록 구현하였다.
하지만 검색없이 행을 선택했을 때는 그 선택한 행의 정보가 잘 나오지만, 검색하고 행을 눌렀을 때 검색전의 인덱스의 정보가 뜬다. 즉, "이영희"를 검색하고 눌렀는데 "홍길동"의 정보가 나오는 것이다.
❗️ 문제가 발생한 이유는 ListSelectionListener
가 동작하는 방식 때문이다. ListSelectionListener
는 선택된 행의 인덱스가 변경될 때마다 호출된다. 그러나 TableRowSorter
를 사용하여 테이블의 행을 필터링하는 경우에는 실제 테이블에서 선택된 행과 필터링된 행이 서로 다를 수 있다. 예를 들어, 이영희를 검색하면 테이블에서는 이영희 한 명만 보이지만, 실제로는 이영희와 김철수 중에서 어떤 행이 선택되었는지 알 수 없다.
✔️ convertRowIndexToModel(): 행의 인덱스 변환
🤔 이 문제를 해결하려면, ListSelectionListener
가 호출될 때 TableRowSorter
에서 선택된 행의 인덱스를 실제 테이블에서의 인덱스로 변환해야 한다. 이를 위해 TableRowSorter
객체의 convertRowIndexToModel()
메서드를 사용한다. 이 메서드는 TableRowSorter
가 적용된 테이블에서 행의 인덱스를 모델에서의 인덱스로 변환해 준다.
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import java.util.regex.PatternSyntaxException;
public class TableSearchExample extends JFrame {
private final JTextField searchField = new JTextField(10);
private final JTable table;
private final DefaultTableModel tableModel;
private final TableRowSorter<DefaultTableModel> sorter;
public TableSearchExample() {
setTitle("JTable Search Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 테이블 모델 초기화
tableModel = new DefaultTableModel();
tableModel.addColumn("이름");
tableModel.addColumn("나이");
tableModel.addRow(new Object[]{"홍길동", 30});
tableModel.addRow(new Object[]{"김철수", 25});
tableModel.addRow(new Object[]{"이영희", 27});
// 테이블 초기화
table = new JTable(tableModel);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
// 검색 필드 초기화
sorter = new TableRowSorter<>(tableModel);
table.setRowSorter(sorter);
searchField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
search();
}
@Override
public void removeUpdate(DocumentEvent e) {
search();
}
@Override
public void changedUpdate(DocumentEvent e) {
search();
}
private void search() {
String searchText = searchField.getText().trim();
// 검색어가 없으면 모든 행을
if (searchText.isEmpty()) {
sorter.setRowFilter(null);
} else {
try {
sorter.setRowFilter(RowFilter.regexFilter("(?i)" + searchText));
} catch (PatternSyntaxException ex) {
// 검색어가 정규식으로 변환되지 않으면 모든 행을 보여줍니다.
sorter.setRowFilter(null);
}
}
}
});
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
// 선택된 행이 변경되면 호출됩니다.
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
// 선택된 행의 인덱스를 모델에서의 인덱스로 변환합니다.
int modelRow = table.convertRowIndexToModel(selectedRow);
// 모델에서 선택된 행의 데이터를 가져옵니다.
String name = tableModel.getValueAt(modelRow, 0).toString();
int age = Integer.parseInt(tableModel.getValueAt(modelRow, 1).toString());
// 선택된 행의 데이터를 출력
System.out.println("Selected row: " + name + ", " + age);
}
}
});
JPanel searchPanel = new JPanel();
searchPanel.add(new JLabel("검색"));
searchPanel.add(searchField);
add(searchPanel, "South");
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
TableSearchExample example = new TableSearchExample();
example.setVisible(true);
}
}