转载:http://www.cnblogs.com/langtianya/archive/2012/09/04/2671173.html
浅析JTable与TableModel、TableCellRenderer、TableCellEditor接口——使用JComboBox显示单元格的值
【github URL:https://github.com/langtianya】 【Other Blog: my.csdn.net/ouchuquan 和 my.oschina.net/u/187589 和 blog.sina.com.cn/u/2430843520】
如同其它的Swing组件,JTable使用MVC(模型、试图、控制器)设计方式,将可视化组件(JTable实例)从其数据(TableModel实现)中分离出来。
·TableModel接口
1、TableModel为JTable提供
·显示的数据
·表格的维数
·表格中每一列所包含的数据类型
·应该显示的列标题
·是否允许编辑指定单元格的值
2、实现TableModel:
TableValues类
import javax.swing.table.AbstractTableModel;
/**
* 注意:一般使用AbstractTableModel创建TableModel的实现,只有少量数据时使用DefaultTableModel,
*/
public class TableValues extends AbstractTableModel{
private static final long serialVersionUID = -8430352919270533604L;
public final static int NAME = 0;
public final static int GENDER = 1;
public final static String[] columnNames = {"姓名", "性别"};
public Object[][] values = {
{"Cannel_2020",true},
{"Lucy",false},
{"韩梅",false},
{"李雷",true},
{"Jim",true}
};
publicint getColumnCount() {
return values[0].length;
}
publicint getRowCount() {
return values.length;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return values[rowIndex][columnIndex];
}
/**
* 获取列名
*/
publicString getColumnName(int column){
return columnNames[column];
}
}
SimpleTableTest类
import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class SimpleTableTest extends JFrame{
private static final long serialVersionUID = -4172876583187222326L;
protected JTable table;
public SimpleTableTest(){
Container pane = getContentPane();
pane.setLayout(newBorderLayout());
TableValues tv = new TableValues();
table= new JTable(tv);
//设置行高
table.setRowHeight(30);
//必须把table放入JScrollPane才会有列名出现
JScrollPane jsp = new JScrollPane(table);
pane.add(jsp,BorderLayout.CENTER);
}
publicstatic void main(String[] args) {
SimpleTableTeststt = new SimpleTableTest();
stt.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
stt.setSize(400,200);
stt.setVisible(true);
}
}
运行结果:
·实现TableCellRenderer(单元格渲染器)接口
1、使表格“性别”一列的单元格出现JComboBox组件
GenderRenderer类
import java.awt.Component;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class GenderRenderer extendsJComboBox implements TableCellRenderer{
privatestatic final long serialVersionUID = -8624401777277852691L;
publicGenderRenderer(){
super();
addItem("男");
addItem("女");
}
publicComponent getTableCellRendererComponent(JTable table, Object value,
booleanisSelected, boolean hasFocus, int row, int column) {
if(isSelected){
setForeground(table.getForeground());
super.setBackground(table.getBackground());
}else{
setForeground(table.getForeground());
setBackground(table.getBackground());
}
booleanisMale = ((Boolean)value).booleanValue();
setSelectedIndex(isMale? 0 : 1);
returnthis;
}
}
2、把SimpleTableTest类的构造函数改变如下:
public SimpleTableTest(){
setTitle("FromCannel_2020‘s blog(CSDN)");
setLayout(newBorderLayout());
TableValuestv = new TableValues();
table= new JTable(tv);
//设置行宽
table.setRowHeight(30);
TableColumnModeltcm= table.getColumnModel();
TableColumntc = tcm.getColumn(TableValues.GENDER);
//设置“性别”列的单元格渲染器(renderer)
tc.setCellRenderer(newGenderRenderer());
//必须把table放入JScrollPane才会有列名出现
JScrollPanejsp = new JScrollPane(table);
add(jsp,BorderLayout.CENTER);
}
运行结果:
3、注意:渲染器实际上并没有像可视化组件添加到Container中那样添加到JTable实例中,即表格中不含有JComboBox实例。此时,是将唯一的JComboBox实例绘制(通过向paint()方法传递Graphics对象)到“性别”一列的每一个单元格所占用的区域中。
4、在TableValues添加如下代码(覆盖AbstractTableModel中的方法),使得JTable实例中单元格可以编辑:
/**
* 设置单元格可以编辑
*/
public booleanisCellEditable(int row, int column){
returntrue;
}
然而此时对“性别”一列的单元格进行编辑,会出现如下情况:
这就得使用到单元格编辑器了。
·实现TableCellEditor(单元格编辑器)接口
1、
import java.awt.Component;
import java.util.EventObject;
import javax.swing.JComboBox;
import javax.swing.JTable;
importjavax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.table.TableCellEditor;
public class GenderEditor extends JComboBoximplements TableCellEditor{
privatestatic final long serialVersionUID = 5860619160549087886L;
//EventListenerList:保存EventListener 列表的类。
privateEventListenerList listenerList = new EventListenerList();
//ChangeEvent用于通知感兴趣的参与者事件源中的状态已发生更改。
privateChangeEvent changeEvent = new ChangeEvent(this);
publicGenderEditor(){
super();
addItem("男");
addItem("女");
//请求终止编辑操作可以包含单元格的JTable收到,也可以从编辑器组件本身(如这里的JComboBox)获得
/*addActionListener(newActionListener(){
publicvoid actionPerformed(ActionEvent e) {
System.out.println("ActionListener");
//如同stopCellEditing,都是调用fireEditingStopped()方法
fireEditingStopped();
}
});*/
}
publicvoid addCellEditorListener(CellEditorListener l) {
listenerList.add(CellEditorListener.class,l);
}
publicvoid removeCellEditorListener(CellEditorListener l) {
listenerList.remove(CellEditorListener.class,l);
}
privatevoid fireEditingStopped(){
CellEditorListenerlistener;
Object[]listeners = listenerList.getListenerList();
for(inti = 0; i < listeners.length; i++){
if(listeners[i]== CellEditorListener.class){
//之所以是i+1,是因为一个为CellEditorListener.class(Class对象),
//接着的是一个CellEditorListener的实例
listener= (CellEditorListener)listeners[i+1];
//让changeEvent去通知编辑器已经结束编辑
<span style="white-space:pre"> </span> //在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值,
//并且把这个值传递给TableValues(TableModel)的setValueAt()
listener.editingStopped(changeEvent);
}
}
}
publicvoid cancelCellEditing() {
}
/**
* 编辑其中一个单元格,再点击另一个单元格时,调用。-------------!!!!!
*/
publicboolean stopCellEditing() {
//可以注释掉下面的fireEditingStopped();,然后在GenderEditor的构造函数中把
//addActionListener()的注释去掉(这时请求终止编辑操作从JComboBox获得),
System.out.println("编辑其中一个单元格,再点击另一个单元格时,调用。");
fireEditingStopped();//请求终止编辑操作从JTable获得
returntrue;
}
/**
* 为一个单元格初始化编辑时,getTableCellEditorComponent被调用
*/
publicComponent getTableCellEditorComponent(JTable table, Object value,
booleanisSelected, int row, int column) {
booleanisMale = ((Boolean)value).booleanValue();
setSelectedIndex(isMale? 0 : 1);
returnthis;
}
/**
* 询问编辑器它是否可以使用 anEvent 开始进行编辑。
*/
publicboolean isCellEditable(EventObject anEvent) {
returntrue;
}
/**
* 如果应该选择正编辑的单元格,则返回true,否则返回 false。
*/
publicboolean shouldSelectCell(EventObject anEvent) {
returntrue;
}
/**
* 返回值传递给TableValue(TableModel)中的setValueAt()方法
*/
publicObject getCellEditorValue() {
returnnew Boolean(getSelectedIndex() == 0 ? true : false);
}
}
2、把SimpleTableTest类的构造函数中
tc.setCellRenderer(new GenderRenderer());
后面加入:
//设置“性别”列的单元格编辑器(editor)
tc.setCellEditor(new GenderEditor());
运行结果:
3、还有一点别忘了再在TableValues加入如下代码(原因:看第4的最后一点)
/**
* 单元格被编辑完后,调用此方法更新值
*/
publicvoid setValueAt(Object value, int row, int column){
values[row][column]= value;
}
4、GenderEditor类的工作流程:
1)、调用TableCellEditor接口中的getTableCellEditorComponent()方法初始化编辑
2)、编辑当前的单元格,再点击另一个单元格时,调用CellEditor中的stopCellEditing(),通过fireEditingStopped()调用到editingStopped()。
3)、在editingStopped方法中,JTable调用getCellEditorValue()取回单元格的值,并且把这个值传递给TableValues(TableModel)的setValueAt()