在JTable的初级教程中往往会提到,使用TableModel的 addTableModelListener方法可以监听单元格数据的变更,在其事件处理函,数tableChanged中,可以通过e.getColumn(),e.getFirstRow(),e.getLastRow(),e.getType()来获取变更发生的位置和变更的类型(插入、更新或删除)。然而该方法存在2个致命的问题:

1.双击单元格使其处于可编辑状态后,即使没有做出任何修改,当单元格失去焦点时,该事件将被激活。

2.通过该事件你可以获取单元格最新的数据,却无法获取原有数据。

经过一番搜索发现该文章已经解决了这个问题Table Cell Listener,作者自己实现了一个单元格监听器TableCellListener,它订阅了指定table的addPropertyChangeListener,根据e.getPropertyName()来识别单元格编辑事件,根据table.isEditing()方法来判断单元格正在编辑还是编辑完毕。如果是正在编辑,则记录单元格位置和原因数据;如果已经编辑完毕,则记录新数据并与原有数据进行比对,如果不一致则说明单元格数据发生了变更,则激活指定响应函数。

测试用例如下:

TableDemo.java

/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ package TableCellListenerTest; /*
* TableDemo.java requires no other files.
*/ import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel; import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent; /**
* TableDemo is just like SimpleTableDemo, except that it
* uses a custom TableModel.
*/
public class TableDemo extends JPanel {
private boolean DEBUG = false; public TableDemo() {
super(new GridLayout(1,0)); JTable table = new JTable(new MyTableModel());
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true); //Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table); //Add the scroll pane to this panel.
add(scrollPane);
Action action = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
TableCellListener tcl = (TableCellListener)e.getSource();
System.out.printf("cell changed%n");
System.out.println("Row : " + tcl.getRow());
System.out.println("Column: " + tcl.getColumn());
System.out.println("Old : " + tcl.getOldValue());
System.out.println("New : " + tcl.getNewValue());
}
};
TableCellListener tcl = new TableCellListener(table, action);
} class MyTableModel extends AbstractTableModel {
private String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
private 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)}
}; 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 < 2) {
return false;
} else {
return true;
}
} /*
* Don't need to implement this method unless your table's
* data can change.
*/
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;
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();
}
});
}
}

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);
}
}
}

Swing-JTable检测单元格数据变更事件的更多相关文章

  1. jqgrid设置单元格数据

    $("#gridid").jqGrid('setCell',rowid,icol,data); rowid为行ID,jqgrid内置的那个,从1开始 icol为列索引,从0开始, ...

  2. QTableWidget 用法总结(只能使用标准的数据模型,并且其单元格数据是QTableWidgetItem的对象)

    QTableWidget是QT程序中常用的显示数据表格的空间,很类似于VC.C#中的DataGrid.说到QTableWidget,就必须讲一下它跟QTabelView的区别了.QTableWidge ...

  3. [转载]Java读取Excel中的单元格数据

    目前网上能找到的读取Excel表格中数据的两种比较好的方案:PageOffice好用开发效率高:POI免费.供大家参考,针对具体情况选择具体方案. 1. PageOffice读取excel impor ...

  4. VBS读取txt文档数据查找Excel中单元格数据符合条件的剪切到工作表2中

    Dim fso,f,a set oExcel = CreateObject( "Excel.Application" ) oExcel.Visible = false '4) 打开 ...

  5. JS遍历表格获取每行数据及每个单元格数据

    /** * 遍历表格获取每行数据及每个单元格数据 * @param tableID 表格ID */ function GetTable(tableID) { var milasUrl = {};//新 ...

  6. python接口自动化测试--数据分离读取Excal指定单元格数据

    上一篇博客讲了怎么批量读取Excal单元格数据,现在咱们说一下怎么读取Excal指定单元格数据. 一.首先建一个Test_Main类 #!/usr/bin/python # -*- coding: U ...

  7. JTable单元格放自定义控件(一)-如何在JTable的单元格放JPanel

    原文链接:http://blog.sina.com.cn/s/blog_7f1c8c710101hdpf.html 最近自己尝试着模仿着实现一款非常有名的进销库存管理系统(智慧记)里面的一个功能.功能 ...

  8. JTable指定单元格加控件

    原文链接:http://blog.csdn.net/transit136/article/details/2133638 JTable可以给表格的某一列加入控件,下面方法可以实现   try{   T ...

  9. zclip结合easyui实现复制datagrid每行特定单元格数据的功能

    在easyui的datagrid里面,实现复制每行特定单元格的功能,关键是想想如何获取到每个单元格的数据,功能是点击按钮"复制",然后复制object的下载地址,截图如下所示: 进 ...

随机推荐

  1. 使用yeoman构建angular应用

    本文将介绍如何亲手来完成一个yeoman的generator,以实现快速构建最适合自己的项目. 本文将实现的generator起名为ngtimo,依照yeoman的命名规矩就叫做generator-n ...

  2. 从用eclipse还是idea所想到的

    最近一直在用idea做开发,刚开始用起来还是不熟,毕竟上次用idea还是研三在某知名互联网实习的时候,在学校和上家公司都用的是eclipse,然后就想,为什么有的公司用eclipse,有的公司用ide ...

  3. C++ Socket学习记录 -3

    WinSocket套接字之间的数据传输 在客户端和服务端建立链接后,接着就是在套接字之间传输数据. 很简单的使用 recv 接收数据,用send发送数据,单但是出现一个问题,不管接收还是发送,数据都是 ...

  4. win7(iis7)localhost可以访问127.0.0.1不可以访问的问题解决办法

    在C:\Windows\System32\drivers\etc文件夹下有hosts文件,用编辑器打开,看看有没有配置127.0.0.1 localhost 把前面的#去掉保存即可!

  5. 关于Alipay支付宝接口(Java版)

    支付宝开发文档:https://b.alipay.com/order/techService.htm 1.alipay 双功能支付简介 2.alipay 提交支付订单 3.alipay 整合双功能支付 ...

  6. Shiro入门

    Shiro是由Apache提供的一个强大且易用的Java安全开源框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到 ...

  7. 解析java中volatile关键字

    在Java多线程编程中经常volatile,有时候这个关键字和synchronized 或者lock经常有人混淆,具体解析如下:  在多线程的环境中会存在成员变量可见性问题: java的每个线程都存在 ...

  8. C++编程规范(不断更新)

    本文总结一些经验教训,以使编出来的代码更为健壮. 1.定义类/结构体的时候不要和系统函数冲突(尤其是不同系统/平台之间移植的时候) 有时候在windows系统下编译没问题,但是在linux系统下就不行 ...

  9. php设计模式--注册器模式

    之前介绍过工厂模式和策略模式有两种方式去调用方法,一种是用工厂方法调用,一种是单例调取实例,那实际上这两种都有一个缺点(可能也不应该称为缺点,也可叫不足),有一个比较好的方法就是已经创建好的这个对象, ...

  10. NTP原理及配置使用

    一.NTP简介 1.NTP简介   NTP(Network Time Protocol,网络时间协议)是用来使网络中的各个计算机时间同步的一种协议.它的用途是把计算机的时钟同步到世界协调时UTC,其精 ...