Java Swing实现JTable检测单元格数据变更事件的方法示例
本文实例讲述了java swing实现jtable检测单元格数据变更事件的方法。分享给大家供大家参考,具体如下:
在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 awtdemo; /* * 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. */ @suppresswarnings("serial") public class tabledemo extends jpanel { private boolean debug = false; @suppresswarnings("unused") 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. */ @suppresswarnings({ "unchecked", "rawtypes" }) 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 - www.jb51.net"); 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 awtdemo; 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); } } }
运行效果:
由图可见,单元格数据修改后,控制台输出内容变更信息!
更多关于java相关内容感兴趣的读者可查看本站专题:《java数据结构与算法教程》、《java字符与字符串操作技巧总结》、《java操作dom节点技巧总结》、《java文件与目录操作技巧汇总》和《java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。