欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java Swing实现JTable检测单元格数据变更事件的方法示例

程序员文章站 2024-04-01 21:40:46
本文实例讲述了java swing实现jtable检测单元格数据变更事件的方法。分享给大家供大家参考,具体如下: 在jtable的初级教程中往往会提到,使用tablemo...

本文实例讲述了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 Swing实现JTable检测单元格数据变更事件的方法示例

由图可见,单元格数据修改后,控制台输出内容变更信息!

更多关于java相关内容感兴趣的读者可查看本站专题:《java数据结构与算法教程》、《java字符与字符串操作技巧总结》、《java操作dom节点技巧总结》、《java文件与目录操作技巧汇总》和《java缓存操作技巧汇总

希望本文所述对大家java程序设计有所帮助。