Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

Java TableCellRenderer와 TableCellEditor의 차이점 완벽 정리

Java Swing에서 JTable을 사용하다 보면 셀을 어떻게 '보여줄지'와 어떻게 '편집할지'를 각각 제어해야 하는 상황이 자주 발생합니다. 이때 사용되는 것이 바로 TableCellRendererTableCellEditor입니다. 두 인터페이스는 이름이 비슷하지만 역할이 명확히 다르며, 이를 이해하면 테이블 UI를 훨씬 유연하게 구성할 수 있습니다.

TableCellRenderer란?

  • TableCellRendererJTable 셀의 값을 화면에 표시(렌더링)하는 컴포넌트를 생성하는 인터페이스입니다.
  • 기본 렌더러는 JLabel을 사용하여 각 테이블 셀의 값을 단순한 텍스트 형태로 보여줍니다.
  • 셀 렌더러는 두 가지 방식으로 지정할 수 있습니다. 첫째, table.setDefaultRenderer() 메서드로 렌더링할 객체의 클래스 타입별로 지정하는 방법과, 둘째, 특정 열(column)에 한정하여 tableColumn.setCellRenderer() 메서드로 지정하는 방법입니다. 또한 열 헤더(header) 전용으로는 tableColumn.setHeaderRenderer() 메서드를 사용할 수 있습니다.
  • TableCellRenderer 인터페이스에는 getTableCellRendererComponent() 메서드 하나만 정의되어 있으며, 이 메서드는 셀의 값, 포커스 여부, 선택 여부, 그리고 해당 값이 위치한 행(row)과 열(column) 정보에 따라 서로 다른 렌더링 컴포넌트를 반환할 수 있습니다.

TableCellEditor란?

  • TableCellEditor는 셀의 값을 편집하기 위한 컴포넌트를 제공하는 인터페이스이며, 기본적으로 JTable의 셀은 편집 가능하도록 설정되어 있습니다.
  • 특정 셀이 편집 가능한지 여부는 TableModelisCellEditable() 메서드를 호출하여 판단합니다.
  • 셀 값의 자료형에 따라 적절한 에디터를 선택할 수 있습니다. 예를 들어 값이 Boolean 타입이라면 JCheckBox(체크박스)를 사용할 수 있고, 더블클릭 시 텍스트 편집 모드로 진입해야 하는 경우라면 JTextField(텍스트 필드)를 사용할 수 있습니다.
  • TableCellEditor 인터페이스에는 getTableCellEditorComponent() 메서드 하나만 정의되어 있으며, 이 메서드 역시 셀의 값, 선택 여부, 행과 열 정보에 따라 서로 다른 편집 컴포넌트를 반환할 수 있습니다.

핵심 차이점 요약

정리하면 TableCellRenderer는 '표시'를 담당하고, TableCellEditor는 '편집'을 담당합니다. 렌더러는 마우스 클릭 없이 항상 화면에 보이는 상태의 UI를 결정하며, 에디터는 사용자가 셀을 더블클릭하거나 편집을 시작했을 때 나타나는 입력 UI를 결정합니다.

예제 코드

아래 예제에서는 첫 번째 열은 편집이 불가능하도록 설정하고, 두 번째 열('Editor')에는 JTextArea를 활용한 커스텀 렌더러와 에디터를 적용했습니다.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableCellRendererEditorTest extends JFrame {
    private JTable table;

    public TableCellRendererEditorTest() {
        setTitle("TableCellRendererEditor Test");

        // 0번 열은 편집 불가, 나머지 열은 편집 가능하도록 설정
        DefaultTableModel dtm = new DefaultTableModel() {
            public boolean isCellEditable(int row, int column) {
                return !(column == 0);
            }
        };

        dtm.setDataVector(
            new Object[][]{{"Table Cell Renderer", "Table Cell Editor"},
                           {"Table Cell Renderer", "Table Cell Editor"}},
            new Object[]{"Renderer", "Editor"}
        );

        table = new JTable(dtm);

        // 'Editor' 열에 커스텀 렌더러와 에디터 적용
        table.getColumn("Editor").setCellRenderer(new TextAreaRenderer());
        table.getColumn("Editor").setCellEditor(new TextAreaEditor());

        table.setRowHeight(80);

        JScrollPane spane = new JScrollPane(table);
        add(spane);
        setSize(400, 275);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        new TableCellRendererEditorTest();
    }
}

// JTextArea 기반 커스텀 셀 렌더러
class TextAreaRenderer extends JScrollPane implements TableCellRenderer {
    JTextArea textarea;

    public TextAreaRenderer() {
        textarea = new JTextArea();
        textarea.setLineWrap(true);
        textarea.setWrapStyleWord(true);
        getViewport().add(textarea);
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {

        if (isSelected) {
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
            textarea.setForeground(table.getSelectionForeground());
            textarea.setBackground(table.getSelectionBackground());
        } else {
            setForeground(table.getForeground());
            setBackground(table.getBackground());
            textarea.setForeground(table.getForeground());
            textarea.setBackground(table.getBackground());
        }
        textarea.setText((String) value);
        textarea.setCaretPosition(0);
        return this;
    }
}

// JTextArea 기반 커스텀 셀 에디터
class TextAreaEditor extends DefaultCellEditor {
    protected JScrollPane scrollpane;
    protected JTextArea textarea;

    public TextAreaEditor() {
        super(new JCheckBox());
        scrollpane = new JScrollPane();
        textarea = new JTextArea();
        textarea.setLineWrap(true);
        textarea.setWrapStyleWord(true);
        scrollpane.getViewport().add(textarea);
    }

    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int column) {
        textarea.setText((String) value);
        return scrollpane;
    }

    public Object getCellEditorValue() {
        return textarea.getText();
    }
}

실행 결과

프로그램을 실행하면 'Renderer' 열은 일반적인 방식으로 값을 표시하며 편집할 수 없고, 'Editor' 열은 클릭하여 편집할 때 JTextArea가 포함된 스크롤 창이 나타나 여러 줄의 텍스트를 자유롭게 수정할 수 있습니다. 이처럼 렌더러와 에디터를 조합하면 JTable의 각 셀에 대해 표시 방식과 편집 방식을 독립적으로 세밀하게 제어할 수 있습니다.