是的,我知道,这听起来很愚蠢。但在此之前,让我先说明一下我的问题。
我有一个带有大量 JTable 的 GUI。 我有各种 DefaultTableModel,每个 JTable 一个。所以,1:1。 对于每个 DefaultTableModel,我都创建了一个 TableModelListener。
为了在 GUI 中插入和删除元素,我只创建了两个方法:addVehicle 和 removeVehicle:
public void addVehicle(final DefaultTableModel model, final String s1, final String s2) {
Runnable addV = new Runnable() {
@Override
public void run() {
model.addRow(new Object[] { s1, s2 });
}
};
SwingUtilities.invokeLater(addV);
}
public void removeVehicle(final DefaultTableModel model, final int index) {
if (row!=null){
String s1= row.elementAt(0).toString();
Runnable removeV = new Runnable() {
@Override
public void run() {
model.removeRow(index);
}
};
SwingUtilities.invokeLater(removeV);
}
}
这些方法很完美,自动识别正确的表模型。
但我有一个必要性:因为我有很多 JTable(以及相应的 DefaultTableModel),并且考虑到当我从表中添加/删除一行时必须通知第三方软件,所以我创建了几个 TableModelListener。
例如监听器1:
TableModelListener listener1 = new TableModelListener() {
public void tableChanged(TableModelEvent e) {
DefaultTableModel m = (DefaultTableModel) e.getSource();
int row = e.getLastRow();
switch (e.getType()) {
case (TableModelEvent.INSERT): {
System.out.println("Insert! Key: "+m.getValueAt(row, 0));
// My agent, the third-party software:
shipperAgent.newTruck((String) m.getValueAt(row, 0));
} break;
case (TableModelEvent.DELETE): {
System.out.println("Delete! Key:"+m.getValueAt(row, 0));
// My agent, the third-party software:
shipperAgent.removeTruck((String)m.getValueAt(row, 0));
} break;
case (TableModelEvent.UPDATE): {
// ...
} break;
}
}
};
另一个例子是监听器 2(我只向您展示一个片段...):
case (TableModelEvent.INSERT): {
System.out.println("Activate! Key: "+m.getValueAt(row, 0));
// My agent, the third-party software:
shipperAgent.activateTruck((String) m.getValueAt(row, 0));
} break;
case (TableModelEvent.DELETE): {
System.out.println("Deactivate! Key:"+m.getValueAt(row, 0));
// My agent, the third-party software:
shipperAgent.deactivateTruck((String)m.getValueAt(row, 0));
} break;
这就是问题所在:插入可以,但删除不行。我知道为什么:当我从 GUI 中删除一行时,该行将被删除,并在开关(案例 TableModelEvent.DELETE)中处理另一行(紧接在我删除的行之后)。此外,如果这一行是最后一行(因为之后没有更多行),我会得到一个 NullPointerException。
如何在监听器中在实际删除行之前使用该行? (或者其他绝妙的方法?)
最佳答案
...when I add/delete a row from a table a third-party software MUST BE INFORMED, I have created several TableModelListener.
如果您看到 TableModelEvent API 无法从事件本身获取数据,您拥有的只是 TableModel这是事件的来源。话虽如此,当表模型事件被触发时,模型中的更改已经完成,您对此无能为力,因此您无法仅仅因为这些赢得了删除的行值而获得删除的行值不再在那里了。
但是,如果您关心的是通知此第三方软件以保持一致性,我想,那么您应该考虑在从表模型中删除行之前这样做.
public void removeVehicle(final DefaultTableModel model, final int index) {
String value = (String)model.getValueAt(index, 0);
shipperAgent.deactivateTruck(value);
// If everything went ok, then remove the row from the table model.
// Of course, do it in the Event Dispatch Thread.
model.removeRow(index);
}
通过这种方式,您甚至可以协调第三方软件和您的应用程序:如果无法停用该行代表的卡车,则不要删除表模型中的该行,而是显示一些错误消息。
<小时/>编辑
But I wanted to centralize communication with the agent (third party software) and separate it from the rest of the code, and not disseminate it.
嗯,这是完全有道理的,我确信有不止一种方法可以实现这一点,但恕我直言,您的设计需要另一种转变才能满足这些要求:
- 第三方代理和您的表模型之间只有一个协调点。
- 不要编写大量实际上执行相同操作的方法:DRY principle
- 在正确的时间处理操作。
- 所有责任必须明确分开。
我在想接近 Mediator pattern 的东西(不完全是这个模式,而是一些接近的模式)可能会帮助您封装协调逻辑并解耦同一事务中涉及的第三方代理和表模型。例如,考虑这个原型(prototype)类:
public abstract class Coordinator {
/*
* protected class members so subclasses can access these directly
*/
protected ShipperAgent shipperAgent;
protected DefaultTableModel tableModel;
public Coordinator(ShipperAgent sa, DefaultTableModel tm) {
shipperAgent = sa;
tableModel = tm;
}
public abstract void notifyAndAddRow(Object[] rowData);
public abstract void notifyAndDeleteRow(int rowIndex);
}
由于您在调用 addVehicle(...)
和 removeVehicle(...)
方法时已成功识别正确的表模型,因此您可能会可以执行相同的操作来识别正确的协调器并委托(delegate)通知第三方代理和更新表模型的任务。例如:
ShipperAgent sa = new ShipperAgent(...);
DefaultTableModel model1 = new DefaultTableModel(...);
DefaultTableModel model2 = new DefaultTableModel(...);
Coordinator coordinator1 = new Coordinator(sa, model1) {
@Override
public void notifyAndAddRow(Object[] rowData) {
this.shipperAgent.newTruck((String) rowData[0]);
this.tableModel.addRow(rowData); // do this in the EDT
}
@Override
public void notifyAndDeleteRow(int rowIndex) {
String truck = (String)this.tableModel.getValueAt(rowIndex, 0);
this.shipperAgent.removeTruck(truck);
this.tableModel.removeRow(rowIndex); // do this in the EDT
}
};
Coordinator coordinator2 = new Coordinator(sa, model2) {
@Override
public void notifyAndAddRow(Object[] rowData) {
this.shipperAgent.activateTruck((String) rowData[0]);
this.tableModel.addRow(rowData); // do this in the EDT
}
@Override
public void notifyAndDeleteRow(int rowIndex) {
String truck = (String)this.tableModel.getValueAt(rowIndex, 0);
this.shipperAgent.removeTruck(truck);
this.tableModel.removeRow(rowIndex); // do this in the EDT
}
};
那么您的 addVehicle(...)
和 removeVehicle(...)
方法可能如下所示:
public void addVehicle(Coordinator coordinator, String s1, String s2) {
coordinator.notifyAndAddRow(new Object[]{s1, s2});
}
public void removeVehicle(Coordinator coordinator, int index) {
coordinator.notifyAndDeleteRow(index);
}
当然,正如我所说,在调用 addVehicle(...)
和 时,您必须设法识别正确的
方法,但我认为您现在已经拥有解决问题所需的抽象。Coordinator
实例。 removeVehicle(...)
关于java - 从 DefaultTableModel 中删除后使用该行的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25529869/