Java Swing實現(xiàn)JTable檢測單元格數(shù)據(jù)變更事件的方法示例
本文實例講述了Java Swing實現(xiàn)JTable檢測單元格數(shù)據(jù)變更事件的方法。分享給大家供大家參考,具體如下:
在JTable的初級教程中往往會提到,使用TableModel的 addTableModelListener方法可以監(jiān)聽單元格數(shù)據(jù)的變更,在其事件處理函,數(shù)tableChanged中,可以通過e.getColumn()
,e.getFirstRow()
,e.getLastRow()
,e.getType()
來獲取變更發(fā)生的位置和變更的類型(插入、更新或刪除)。然而該方法存在2個致命的問題:
1.雙擊單元格使其處于可編輯狀態(tài)后,即使沒有做出任何修改,當(dāng)單元格失去焦點時,該事件將被激活。
2.通過該事件你可以獲取單元格最新的數(shù)據(jù),卻無法獲取原有數(shù)據(jù)。
經(jīng)過一番搜索發(fā)現(xiàn)該文章已經(jīng)解決了這個問題Table Cell Listener,作者自己實現(xiàn)了一個單元格監(jiān)聽器TableCellListener,它訂閱了指定table的addPropertyChangeListener,根據(jù)e.getPropertyName()
來識別單元格編輯事件,根據(jù)table.isEditing()
方法來判斷單元格正在編輯還是編輯完畢。如果是正在編輯,則記錄單元格位置和原因數(shù)據(jù);如果已經(jīng)編輯完畢,則記錄新數(shù)據(jù)并與原有數(shù)據(jù)進(jìn)行比對,如果不一致則說明單元格數(shù)據(jù)發(fā)生了變更,則激活指定響應(yīng)函數(shù)。
測試用例如下:
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.dbjr.com.cn"); 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); //這里應(yīng)對oldValue為null的情況做處理,否則將導(dǎo)致原值與新值均為空時仍被視為值改變 if(oldValue == null) oldValue = ""; newValue = null; } /* * Update the Cell history when necessary */ private void processEditingStopped() { newValue = table.getModel().getValueAt(row, column); //這里應(yīng)對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); } } }
運行效果:
由圖可見,單元格數(shù)據(jù)修改后,控制臺輸出內(nèi)容變更信息!
更多關(guān)于java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java字符與字符串操作技巧總結(jié)》、《Java操作DOM節(jié)點技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設(shè)計有所幫助。
- Java Swing窗體關(guān)閉事件的調(diào)用關(guān)系
- Java Swing中JList選擇事件監(jiān)聽器ListSelectionListener用法示例
- JavaSwing基礎(chǔ)之Layout布局相關(guān)知識詳解
- Java Swing實現(xiàn)坦克大戰(zhàn)游戲
- Java Swing最詳細(xì)基礎(chǔ)知識總結(jié)
- Java實戰(zhàn)之基于swing的QQ郵件收發(fā)功能實現(xiàn)
- Eclipse+Java+Swing實現(xiàn)學(xué)生成績管理系統(tǒng)的實例代碼
- Java Swing 只關(guān)閉當(dāng)前窗體的實現(xiàn)
- java swing 創(chuàng)建一個簡單的QQ界面教程
- Java基礎(chǔ)學(xué)習(xí)之Swing事件監(jiān)聽
相關(guān)文章
Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐
本文主要介紹了Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09SpringBoot啟動嵌入式Tomcat的實現(xiàn)步驟
本文主要介紹了淺談SpringBoot如何啟動嵌入式Tomcat,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08IDEA解決src和resource下創(chuàng)建多級目錄的操作
這篇文章主要介紹了IDEA解決src和resource下創(chuàng)建多級目錄的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Spring中的@ControllerAdvice和ResponseBodyAdvice詳解
這篇文章主要介紹了Spring中的@ControllerAdvice和ResponseBodyAdvice詳解,@ControllerAdvice作用于@Controller修飾的類里面的所有方法,ResponseBodyAdvice作用于@ResponseBody注解修飾的方法,它可以對這些方法的返回值進(jìn)行修改,需要的朋友可以參考下2024-01-01java如何實現(xiàn)嵌套對象轉(zhuǎn)大map(扁平化)
這篇文章主要介紹了java如何實現(xiàn)嵌套對象轉(zhuǎn)大map(扁平化),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10Java 中HttpURLConnection附件上傳的實例詳解
這篇文章主要介紹了Java 中HttpURLConnection附件上傳的實例詳解的相關(guān)資料,希望通過本文大家能掌握這樣的知識內(nèi)容,需要的朋友可以參考下2017-09-09