[Java] Swing JTable 자동검색 , 행 선택

JTI·2023년 3월 1일
0

📌 Code list

목록 보기
50/55
post-thumbnail

✏️ JTable 자동검색


JTable의 값을 검색할때 버튼없이 자동검색하려고 할 때 DocumentListenerTableRowSorter 를 쓰면 된다.

✔️ 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 가 적용된 테이블에서 행의 인덱스를 모델에서의 인덱스로 변환해 준다.

✏️ 자동검색 + 행 선택 code

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);
    }
}
profile
Fill in my own colorful colors🎨

0개의 댓글