前几篇文章介绍了JTable的基本用法,本文实现一个简单的JTable,算是前文的一个总结,并造福供拷贝党们。
一、主要功能
1.数据的增删改;
2.渲染器:“Vegetarian”列存放布尔值,以checkBox形式显示;“Sport”列存放字符串,以comboBox形式显示;
3.编辑器:“Name”的编辑器实现一个按钮,按下时弹出对话框;
4.ToolTip:各列和各单元格均具有自己的ToolTip,且单元格ToolTip与其值相关;
5.事件:检测单元格值的变更,并输出旧值、新值和单元格坐标。
二、程序设计
本程序根据功能可分为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:实现一个渲染器,可定制单元格的配色。
三、程序代码
Gui.java
packageDefaultTableModelDemo;importjava.awt.Dimension;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjavax.swing.AbstractAction;importjavax.swing.Action;importjavax.swing.DefaultCellEditor;importjavax.swing.JButton;importjavax.swing.JCheckBox;importjavax.swing.JComboBox;importjavax.swing.JComponent;importjavax.swing.JFrame;importjavax.swing.JPanel;importjavax.swing.JScrollPane;importjavax.swing.JTextField;importjavax.swing.table.DefaultTableCellRenderer;importjavax.swing.table.TableColumn;importJButtonTableExample.ButtonEditor;importJButtonTableExample.ButtonRenderer;public class Gui extendsJPanel {private Object[] tmpRow = {"tmpName", "tmpDescription"};privateMyJTable table;privateJButton addBtn;privateJButton delBtn;privateMyTableModel model ;publicGui() {
table= newMyJTable();
table.setPreferredScrollableViewportSize(new Dimension(500, 300));
table.setFillsViewportHeight(true);//Create the scroll pane and add the table to it.
JScrollPane scrollPane = newJScrollPane(table);//scrollPane.setPreferredSize(new Dimension(500, 600));//scrollPane.set//Add the scroll pane to this panel.
add(scrollPane);//set tableModel and data
model = newMyTableModel();
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(newButtonRenderer());//添加编辑器
table.getColumn("Name").setCellEditor( newButtonEditor());//添加按钮
addBtn = new JButton("增加");
addBtn.addActionListener(newActionListener() {
@Overridepublic voidactionPerformed(ActionEvent arg0) {//TODO Auto-generated method stub
model.addRow(tmpRow);
}
});
delBtn= new JButton("删除");
delBtn.addActionListener(newActionListener() {
@Overridepublic voidactionPerformed(ActionEvent arg0) {//TODO Auto-generated method stub
int rowIndex =table.getSelectedRow();if(rowIndex != -1)
model.removeRow(rowIndex);
}
});
add(addBtn);
add(delBtn);
addDataChangeListener();//设置列
setSportsColumn();
}private voidsetSportsColumn(){
String [] itmes= {"Snowboarding", "Rowing", "Knitting", "Speed reading", "Pool"};
JComboBox comboBox = new JComboBox(itmes);
DefaultTableCellRenderer renderer=
newDefaultTableCellRenderer();
renderer.setToolTipText("Click for combo box");
setColumn("Sport", comboBox, renderer);
TableColumn col= table.getColumn("Sport");//setToolTipText("favorit sport is " + );
}public voidsetColumn(String colName, Object editor, Object renderer) {int index =table.getColumnModel().getColumnIndex(colName);
TableColumn modeColumn=table.getColumnModel().getColumn(index);if (editor instanceofJComponent) {
setEditor(modeColumn, (JComponent)editor);
}else if (editor instanceofDefaultCellEditor) {
modeColumn.setCellEditor((DefaultCellEditor)editor);
}if (renderer instanceofDefaultTableCellRenderer) {
modeColumn.setCellRenderer((DefaultTableCellRenderer)renderer);
}else if (renderer instanceofButtonRenderer) {
modeColumn.setCellRenderer((ButtonRenderer)renderer);
}
}protected voidsetEditor(TableColumn column, JComponent component){if(component instanceofJTextField )
column.setCellEditor(newDefaultCellEditor((JTextField) component));else if(component instanceofJComboBox )
column.setCellEditor(new DefaultCellEditor((JComboBox) component));else if(component instanceofJCheckBox )
column.setCellEditor(newDefaultCellEditor((JCheckBox) component));
}private voidaddDataChangeListener(){//检测单元格数据变更
Action action = newAbstractAction()
{public voidactionPerformed(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= newTableCellListener(table, action);
System.out.printf("cell changed%n");
}private static voidcreateAndShowGUI() {//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 = newGui();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);//Display the window.
frame.pack();
frame.setVisible(true);
}public static voidmain(String[] args) {//Schedule a job for the event-dispatching thread://creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(newRunnable() {public voidrun() {
createAndShowGUI();
}
});
}
}
MyTableModel.java
packageDefaultTableModelDemo;importjavax.swing.table.DefaultTableModel;public class MyTableModel extendsDefaultTableModel{
@Overridepublic Class> getColumnClass(intcolumnIndex) {if (columnIndex == 4)return Boolean.class;return super.getColumnClass(columnIndex);
}
}
MyJTable.java
packageDefaultTableModelDemo;importjava.awt.event.MouseEvent;importjavax.swing.JTable;importjavax.swing.table.JTableHeader;importjavax.swing.table.TableModel;public class MyJTable extendsJTable{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.
publicString 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);
}returntip;
}//Implement table header tool tips.
protectedJTableHeader createDefaultTableHeader() {return newJTableHeader(columnModel) {publicString getToolTipText(MouseEvent e) {
String tip= null;
java.awt.Point p=e.getPoint();int index =columnModel.getColumnIndexAtX(p.x);int realIndex =columnModel.getColumn(index).getModelIndex();returncolumnToolTips[realIndex];
}
};
}
}
TableCellListener.java
packageDefaultTableModelDemo;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 implementsPropertyChangeListener, Runnable
{privateJTable table;privateAction action;private introw;private intcolumn;privateObject oldValue;privateObject newValue;/*** Create a TableCellListener.
*
*@paramtable the table to be monitored for data changes
*@paramaction the Action to invoke when cell data is changed*/
publicTableCellListener(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.
*
*@paramrow the row of the changed cell
*@paramcolumn the column of the changed cell
*@paramoldValue the old data of the changed cell
*@paramnewValue the new data of the changed cell*/
private TableCellListener(JTable table, int row, intcolumn, 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
*
*@returnthe column that was edited*/
public intgetColumn()
{returncolumn;
}/*** Get the new value in the cell
*
*@returnthe new value in the cell*/
publicObject getNewValue()
{returnnewValue;
}/*** Get the old value of the cell
*
*@returnthe old value of the cell*/
publicObject getOldValue()
{returnoldValue;
}/*** Get the row that was last edited
*
*@returnthe row that was edited*/
public intgetRow()
{returnrow;
}/*** Get the table of the cell that was changed
*
*@returnthe table of the cell that was changed*/
publicJTable getTable()
{returntable;
}//
//Implement the PropertyChangeListener interface//@Overridepublic voidpropertyChange(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 voidprocessEditingStarted()
{//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.*/@Overridepublic voidrun()
{
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 voidprocessEditingStopped()
{
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= newTableCellListener(
getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
ActionEvent event= newActionEvent(
tcl,
ActionEvent.ACTION_PERFORMED,"");
action.actionPerformed(event);
}
}
}
ButtonEditor.java
packageJButtonTableExample;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjavax.swing.DefaultCellEditor;importjavax.swing.JButton;importjavax.swing.JCheckBox;importjavax.swing.JComponent;importjavax.swing.JOptionPane;importjavax.swing.JTable;public class ButtonEditor extendsDefaultCellEditor {protected JButton button;//represent the cellEditorComponent
private String cellValue;//保存cellEditorValue
publicButtonEditor() {super(newJCheckBox());
button= newJButton();
button.setOpaque(true);
button.addActionListener(newActionListener() {public voidactionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(button, cellValue+ ": Ouch!");//刷新渲染器
fireEditingStopped();
}
});
}publicJComponent getTableCellEditorComponent(JTable table, Object value,boolean isSelected, int row, intcolumn) {//value 源于单元格数值
cellValue = (value == null) ? "": value.toString();returnbutton;
}publicObject getCellEditorValue() {return newString(cellValue);
}
}
ButtonRenderer.java
packageJButtonTableExample;importjava.awt.Color;importjavax.swing.JButton;importjavax.swing.JComponent;importjavax.swing.JTable;importjavax.swing.table.TableCellRenderer;public class ButtonRenderer extends JButton implementsTableCellRenderer {publicJComponent getTableCellRendererComponent(JTable table, Object value,boolean isSelected, boolean hasFocus, int row, intcolumn) {//value 源于editor
String text = (value == null) ? "": value.toString();//按钮文字
setText(text);//单元格提示
setToolTipText(text);//背景色
setBackground(Color.BLACK);//前景色
setForeground(Color.green);return this;
}
}
运行效果如下: