前幾篇文章介紹了JTable的基本用法,本文實現一個簡單的JTable,算是前文的一個總結,並造福供拷貝黨們。html
1、主要功能ide
1.數據的增刪改;post
2.渲染器:「Vegetarian」列存放布爾值,以checkBox形式顯示;「Sport」列存放字符串,以comboBox形式顯示;ui
3.編輯器:「Name」的編輯器實現一個按鈕,按下時彈出對話框;this
4.ToolTip:各列和各單元格均具備本身的ToolTip,且單元格ToolTip與其值相關;url
5.事件:檢測單元格值的變動,並輸出舊值、新值和單元格座標。spa
2、程序設計
本程序根據功能可分爲6部分,以6個類來實現,分別是:
Gui.java:實現GUI,成員有:1個JTable,2個按鈕;
MyJTable.java:繼承自JTable,重載2個方法:getToolTipText和createDefaultTableHeader,分別實現單元格和表頭的toolTip;
MyTableModel.java:繼承自DefaultTableModel,重載1個方法:getColumnClass,實現布爾值的checkBox形式顯示。表格的基本功能均已被DefaultTableModel類實現,直接使用就好。若是你還須要對單元格可訪問性等細節進行精確控制,能夠重載相關方法。
TableCellListener.java:實現對單元格數據變動的檢測。這是經過表格的addPropertyChangeListener方法實現的,而不是基於tableModel的addTableModelListener方法。後者的不足之處在前文中已經分析。
ButtonEditor.java:實現一個基於按鈕的編輯器,被按下時彈出對話框;
ButtonRenderer.java:實現一個渲染器,可定製單元格的配色。
3、程序代碼
Gui.java
package DefaultTableModelDemo; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import JButtonTableExample.ButtonEditor; import JButtonTableExample.ButtonRenderer; public class Gui extends JPanel { private Object[] tmpRow = {"tmpName", "tmpDescription"}; private MyJTable table; private JButton addBtn; private JButton delBtn; private MyTableModel model ; public Gui() { table = new MyJTable(); table.setPreferredScrollableViewportSize(new Dimension(500, 300)); table.setFillsViewportHeight(true); //Create the scroll pane and add the table to it. JScrollPane scrollPane = new JScrollPane(table); //scrollPane.setPreferredSize(new Dimension(500, 600)); //scrollPane.set //Add the scroll pane to this panel. add(scrollPane); //set tableModel and data model = new MyTableModel(); String[] columnNames = {"Name", "Description", "Sport", "# of Years", "Vegetarian"}; Object[][] data = { {"Kathy", "Smith", "Snowboarding", new Integer(5), new Boolean(false)}, {"John", "Doe", "Rowing", new Integer(3), new Boolean(true)}, {"Sue", "Black", "Knitting", new Integer(2), new Boolean(false)}, {"Jane", "White", "Speed reading", new Integer(20), new Boolean(true)}, {"Joe", "Brown", "Pool", new Integer(10), new Boolean(false)} }; model.setDataVector(data, columnNames); table.setModel(model); //添加渲染器 table.getColumn("Name").setCellRenderer(new ButtonRenderer()); //添加編輯器 table.getColumn("Name").setCellEditor( new ButtonEditor()); //添加按鈕 addBtn = new JButton("增長"); addBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub model.addRow(tmpRow); } }); delBtn = new JButton("刪除"); delBtn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub int rowIndex = table.getSelectedRow(); if(rowIndex != -1) model.removeRow(rowIndex); } }); add(addBtn); add(delBtn); addDataChangeListener(); //設置列 setSportsColumn(); } private void setSportsColumn(){ String [] itmes = {"Snowboarding", "Rowing", "Knitting", "Speed reading", "Pool"}; JComboBox<String> comboBox = new JComboBox<String>(itmes); DefaultTableCellRenderer renderer = new DefaultTableCellRenderer(); renderer.setToolTipText("Click for combo box"); setColumn("Sport", comboBox, renderer); TableColumn col = table.getColumn("Sport"); //setToolTipText("favorit sport is " + ); } public void setColumn(String colName, Object editor, Object renderer) { int index = table.getColumnModel().getColumnIndex(colName); TableColumn modeColumn = table.getColumnModel().getColumn(index); if (editor instanceof JComponent) { setEditor(modeColumn, (JComponent)editor); } else if (editor instanceof DefaultCellEditor) { modeColumn.setCellEditor((DefaultCellEditor)editor); } if (renderer instanceof DefaultTableCellRenderer) { modeColumn.setCellRenderer((DefaultTableCellRenderer)renderer); } else if (renderer instanceof ButtonRenderer) { modeColumn.setCellRenderer((ButtonRenderer)renderer); } } protected void setEditor(TableColumn column, JComponent component){ if(component instanceof JTextField ) column.setCellEditor(new DefaultCellEditor((JTextField) component)); else if(component instanceof JComboBox ) column.setCellEditor(new DefaultCellEditor((JComboBox<String>) component)); else if(component instanceof JCheckBox ) column.setCellEditor(new DefaultCellEditor((JCheckBox) component)); } private void addDataChangeListener(){ //檢測單元格數據變動 Action action = new AbstractAction() { public void actionPerformed(ActionEvent e) { TableCellListener tcl = (TableCellListener)e.getSource(); int row = tcl.getRow(); int col = tcl.getColumn(); Object oldValue = tcl.getOldValue(); //if(oldValue == null) //oldValue = ""; Object newValue = tcl.getNewValue(); //if(newValue == null) //newValue = ""; System.out.printf("cell changed at [%d,%d] : %s -> %s%n",row, col, oldValue, newValue); } }; @SuppressWarnings("unused") TableCellListener tcl1 = new TableCellListener(table, action); System.out.printf("cell changed%n"); } private static void createAndShowGUI() { //Create and set up the window. JFrame frame = new JFrame("Gui"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. Gui newContentPane = new Gui(); 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(); } }); } }
MyTableModel.java
package DefaultTableModelDemo; import javax.swing.table.DefaultTableModel; public class MyTableModel extends DefaultTableModel{ @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 4) return Boolean.class; return super.getColumnClass(columnIndex); } }
MyJTable.java
package DefaultTableModelDemo; import java.awt.event.MouseEvent; import javax.swing.JTable; import javax.swing.table.JTableHeader; import javax.swing.table.TableModel; public class MyJTable extends JTable{ protected String[] columnToolTips = {null, null, "The person's favorite sport to participate in is : ", "The number of years the person has played the sportis : ", "If checked, the person eats no meat"}; //Implement table cell tool tips. public String getToolTipText(MouseEvent e) { String tip = null; java.awt.Point p = e.getPoint(); int rowIndex = rowAtPoint(p); int colIndex = columnAtPoint(p); int realColumnIndex = convertColumnIndexToModel(colIndex); if(rowIndex < 0) { //System.out.printf("abnormal rowIndex: %n", rowIndex); return null; } if (realColumnIndex == 2) { //Sport column tip = columnToolTips[2] + getValueAt(rowIndex, colIndex); } else if (realColumnIndex == 3) { //Years column tip = columnToolTips[3] + getValueAt(rowIndex, colIndex); }else if (realColumnIndex == 4) { //Veggie column TableModel model = getModel(); String firstName = (String)model.getValueAt(rowIndex,0); String lastName = (String)model.getValueAt(rowIndex,1); Boolean veggie = (Boolean)model.getValueAt(rowIndex,4); if (Boolean.TRUE.equals(veggie)) { tip = firstName + " " + lastName + " is a vegetarian"; } else { tip = firstName + " " + lastName + " is not a vegetarian"; } } else { //You can omit this part if you know you don't //have any renderers that supply their own tool //tips. tip = super.getToolTipText(e); } return tip; } //Implement table header tool tips. protected JTableHeader createDefaultTableHeader() { return new JTableHeader(columnModel) { public String getToolTipText(MouseEvent e) { String tip = null; java.awt.Point p = e.getPoint(); int index = columnModel.getColumnIndexAtX(p.x); int realIndex = columnModel.getColumn(index).getModelIndex(); return columnToolTips[realIndex]; } }; } }
TableCellListener.java
package DefaultTableModelDemo; import java.awt.event.*; import javax.swing.*; import java.beans.*; /* * This class listens for changes made to the data in the table via the * TableCellEditor. When editing is started, the value of the cell is saved * When editing is stopped the new value is saved. When the oold and new * values are different, then the provided Action is invoked. * * The source of the Action is a TableCellListener instance. */ public class TableCellListener implements PropertyChangeListener, Runnable { private JTable table; private Action action; private int row; private int column; private Object oldValue; private Object newValue; /** * Create a TableCellListener. * * @param table the table to be monitored for data changes * @param action the Action to invoke when cell data is changed */ public TableCellListener(JTable table, Action action) { this.table = table; this.action = action; this.table.addPropertyChangeListener( this ); } /** * Create a TableCellListener with a copy of all the data relevant to * the change of data for a given cell. * * @param row the row of the changed cell * @param column the column of the changed cell * @param oldValue the old data of the changed cell * @param newValue the new data of the changed cell */ private TableCellListener(JTable table, int row, int column, Object oldValue, Object newValue) { this.table = table; this.row = row; this.column = column; this.oldValue = oldValue; this.newValue = newValue; } /** * Get the column that was last edited * * @return the column that was edited */ public int getColumn() { return column; } /** * Get the new value in the cell * * @return the new value in the cell */ public Object getNewValue() { return newValue; } /** * Get the old value of the cell * * @return the old value of the cell */ public Object getOldValue() { return oldValue; } /** * Get the row that was last edited * * @return the row that was edited */ public int getRow() { return row; } /** * Get the table of the cell that was changed * * @return the table of the cell that was changed */ public JTable getTable() { return table; } // // Implement the PropertyChangeListener interface // @Override public void propertyChange(PropertyChangeEvent e) { // A cell has started/stopped editing if ("tableCellEditor".equals(e.getPropertyName())) { if (table.isEditing()){ //System.out.printf("tableCellEditor is editing..%n"); processEditingStarted(); } else{ //System.out.printf("tableCellEditor editing stopped..%n"); processEditingStopped(); } } } /* * Save information of the cell about to be edited */ private void processEditingStarted() { // The invokeLater is necessary because the editing row and editing // column of the table have not been set when the "tableCellEditor" // PropertyChangeEvent is fired. // This results in the "run" method being invoked SwingUtilities.invokeLater( this ); } /* * See above. */ @Override public void run() { row = table.convertRowIndexToModel( table.getEditingRow() ); column = table.convertColumnIndexToModel( table.getEditingColumn() ); oldValue = table.getModel().getValueAt(row, column); //這裏應對oldValue爲null的狀況作處理,不然將致使原值與新值均爲空時仍被視爲值改變 if(oldValue == null) oldValue = ""; newValue = null; } /* * Update the Cell history when necessary */ private void processEditingStopped() { newValue = table.getModel().getValueAt(row, column); //這裏應對newValue爲null的狀況作處理,不然後面會拋出異常 if(newValue == null) newValue = ""; // The data has changed, invoke the supplied Action if (! newValue.equals(oldValue)) { // Make a copy of the data in case another cell starts editing // while processing this change TableCellListener tcl = new TableCellListener( getTable(), getRow(), getColumn(), getOldValue(), getNewValue()); ActionEvent event = new ActionEvent( tcl, ActionEvent.ACTION_PERFORMED, ""); action.actionPerformed(event); } } }
ButtonEditor.java
package JButtonTableExample; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JTable; public class ButtonEditor extends DefaultCellEditor { protected JButton button;//represent the cellEditorComponent private String cellValue;//保存cellEditorValue public ButtonEditor() { super(new JCheckBox()); button = new JButton(); button.setOpaque(true); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(button, cellValue + ": Ouch!"); //刷新渲染器 fireEditingStopped(); } }); } public JComponent getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { //value 源於單元格數值 cellValue = (value == null) ? "" : value.toString(); return button; } public Object getCellEditorValue() { return new String(cellValue); } }
ButtonRenderer.java
package JButtonTableExample; import java.awt.Color; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; public class ButtonRenderer extends JButton implements TableCellRenderer { public JComponent getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { //value 源於editor String text = (value == null) ? "" : value.toString(); //按鈕文字 setText(text); //單元格提示 setToolTipText(text); //背景色 setBackground(Color.BLACK); //前景色 setForeground(Color.green); return this; } }
運行效果以下: