본문 바로가기

Java SE/JTable 01

JTable 01

/*
 * TableDemo.java requires no other files.
 */

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

/**
 * TableDemo is just like SimpleTableDemo, except that it
 * uses a custom TableModel.
 * 다음과 같은 클래스, 메소드를 사용하여 컬럼의 폭을 조정하거나, 선택영역에 대한 정보를
 * 즉시 알 수 있으며, Table과 Model간 데이터 동기화 하고, 셀에 CheckBox가 나타나게 할 수 있으며,
 * 테이블 셀에 ComboBox를 사용하는 예를 보이고, 셀을 더블클릭하는 경우 나타나는 CellEditor를
 * 지정하는 방법을 제시한다.
 * JTableHeader, AbstractTableModel, ListSelectionModel, ListSelectionListener, CellEditor,
 * TableColumn, getValueAt(), setValueAt(), fireTableCellUpdated(), getColumnClass()
 * isCellEditable(), setPreferredWidth()
 */
public class TableDemo extends JPanel {
    private boolean DEBUG = true;
 JTable table;
 MyTableModel model;
 JTableHeader header;

    public TableDemo() {
        super(new GridLayout(1,0));
   model = new MyTableModel();
        table = new JTable(model);
  // 이부분은 테이블 헤더를 사용하는 방법을 제시할 뿐이며  문맥상 의미는 없다
  // 테이블헤더를 이용하지 않는다면 이부분을 제거해도 됨
  header = table.getTableHeader();
  header.addMouseListener(new MouseAdapter() {
   public void mouseReleased(MouseEvent e) {
    System.out.println("Mouse Clicked");
    model.setValueAt("Hello", 0, 1);
   }
  });// 테이블 헤더 사용예 끝

        table.setPreferredScrollableViewportSize(new Dimension(500, 70));

  table.setRowSelectionAllowed( true );
  table.setColumnSelectionAllowed( true );

  table.setSelectionForeground( Color.white );
  table.setSelectionBackground( Color.red );

  // 2번째 컬럼을 위한 CellEditor를 설정한다
  // 즉, 2번째 컬럼을 더블 클릭하면 해당 셀값을 편집할 수 있는 JTextField가 나타나게 한다
  // CellEditor가 필요없다면 이 부분을 제거해도 됨
  TableColumn lastNameColumn = table.getColumnModel().getColumn(1);
  JTextField tf = new JTextField();
  tf.addActionListener(new ActionListener(){
   public void actionPerformed(ActionEvent e) {
    Object o = e.getSource();
    String value = null;
    if(o instanceof JTextField) value=((JTextField)o).getText();
    int row = table.getSelectedRow();
    int col = table.getSelectedColumn();
    System.out.println("Last Name("+row+", "+col+") changed-->"+value);
    //model에게 테이블 셀의 데이터가 변경되었음 알려서 model의 setValueAt(row, col)메소드가
    //실행되도록 하여 실제 모델의 데이터가 변경될 수 있게 한다.
    model.fireTableCellUpdated(row, col);
   }
  });
  lastNameColumn.setCellEditor(new DefaultCellEditor(tf));
  //두번째 컬럼을 위한 CellEditor 설정 끝
 
  //ListSelectionModel에 ListSelectionListener를 설정하여 셀 선택을 감지한다.
  //셀 선택 즉시 해당 셀의 정보를 구할 필요가 없다면 이부분을 제거해도 됨
  ListSelectionModel sm = table.getSelectionModel();
  sm.addListSelectionListener(new ListSelectionListener() {
   public void valueChanged(ListSelectionEvent e) {
    if(e.getValueIsAdjusting()) return;
    ListSelectionModel lsm = (ListSelectionModel)e.getSource();
    if(lsm.isSelectionEmpty()){
    }else{
     int minRow = lsm.getMinSelectionIndex();
     int maxRow = lsm.getMaxSelectionIndex();
     int minCol = table.getSelectedColumn();
     int maxCol = minCol + table.getSelectedColumnCount();
     System.out.println("선택범위:"+minRow+","+minCol+" ~ "+maxRow+","+(maxCol-1));
    }
   }
  });//ListSelectionListener설정 끝
 

  // 테이블 컬럼의 폭을 변경하는 예
  // 테이블 컬럼의 폭을 변경할 필요가 없다면 이부분을 제거해도 됨
  TableColumn column = null;
  for(int i=0;i<table.getColumnCount();i++) {
   column = table.getColumnModel().getColumn(i);
   if(i==2) column.setPreferredWidth(100); // default width is 75.
   else column.setPreferredWidth(50);
  }// 테이블 컬럼의 폭을 변경하는 예 끝

  // 테이블 셀에 ComboBox를 설정하는 방법
  // ComboBox를 셀에 설정할 필요가 없다면 이 부분을 제거해도 됨
  column = table.getColumnModel().getColumn(2);
  JComboBox cb = new JComboBox();
  cb.addItem("Snowboarding");
  cb.addItem("Rowing");  
  cb.addItem("Chasing toddlers");
  cb.addItem("Speed reading");
  cb.addItem("Teaching high school");  
  cb.addItem("None");
  column.setCellEditor(new DefaultCellEditor(cb));
  // 테이블 셀에 ComboBox를 설정하는 방법 끝
 
  // 테이블 행의 높이 설정법
  table.setRowHeight(30);
  // 테이블 행의 높이 설정끝

  //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Add the scroll pane to this panel.
        add(scrollPane);
    } // end of constructor

    class MyTableModel extends AbstractTableModel {
        private String[] columnNames = {"First Name", "Last Name", "Sport", "# of Years", "Vegetarian", "Picture"};
        private Object[][] data = {
            {"Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(false), new ImageIcon("비.jpg")},
            {"Alison", "Huml",  "Rowing", new Integer(3), new Boolean(true), new ImageIcon("비.jpg")},
            {"Kathy", "Walrath", "Knitting", new Integer(2), new Boolean(false), new ImageIcon("비.jpg")},
            {"Sharon", "Zakhour",  "Speed reading", new Integer(20), new Boolean(true), new ImageIcon("비.jpg")},
            {"Philip", "Milne", "Pool", new Integer(10), new Boolean(false), new ImageIcon("비.jpg")},
            {"Isaac", "Rabinovitch", "Nitpicking", new Integer(1000), new Boolean(false), new ImageIcon("비.jpg")}
        };

        public int getColumnCount() {
           return columnNames.length;
        }

        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        /*
         * JTable uses this method to determine the default renderer/
         * editor for each cell.  If we didn't implement this method,
         * then the last column would contain text ("true"/"false"),
         * rather than a check box.
         */
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        /*
         * Don't need to implement this method unless your table's
         * editable.
         */
        public boolean isCellEditable(int row, int col) {
            //Note that the data/cell address is constant,
            //no matter where the cell appears onscreen.
            if (col < 1) {
                return false;
            } else {
                return true;
            }
        }

        /*
         * Don't need to implement this method unless your table's
         * data can change.
   * 테이블의 특정 셀을 마우스로 더블 클릭하여 데이터를 수정한 후에,
   * model.fireTableCellUpdated(row, col) 메소드를 호출하면 모델의 데이터가 갱신된다.
   * 즉, Table --> Model 갱신
   * 반대로, 모델의 setValueAt(Object, row, col) 메소드를 직접 호출하여 모델의 데이터를
   * 수정한 후에 이 메소드 안에서 fireTableCellUpdated(row, col)메소드를 호출하면
   * 테이블의 해당 셀이 갱신된다. 즉, Model --> Table 갱신
         */
        public void setValueAt(Object value, int row, int col) {
            if (DEBUG) {
                System.out.println("Setting value at " + row + "," + col
                                   + " to " + value
                                   + " (an instance of "
                                   + value.getClass() + ")");
            }

            data[row][col] = value;

   // view(JTable)에 알려서 수정된 모델의 데이터를 테이블에 반영한다.
            fireTableCellUpdated(row, col);

            if (DEBUG) {
                System.out.println("New value of data:");
                printDebugData();
            }
        }

        private void printDebugData() {
            int numRows = getRowCount();
            int numCols = getColumnCount();

            for (int i=0; i < numRows; i++) {
                System.out.print("    row " + i + ":");
                for (int j=0; j < numCols; j++) {
                    System.out.print("  " + data[i][j]);
                }
                System.out.println();
            }
            System.out.println("--------------------------");
        }
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("TableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        TableDemo newContentPane = new TableDemo();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}