克隆后编辑 JTable 单元格
Editing JTable cells after being cloned
我正在做一个工作项目,我遇到了一个不寻常的问题。我有一个 JTable,用户可以用数据填充它。在我的实际代码中,有一个 "add row" 按钮让用户填写一些 GUI,该信息生成行。
此功能的另一个重要功能是用于克隆行的按钮。由于添加行的过程可能非常耗时(有很多字段需要填写),如果用户只需要添加一个新行且有 1 个不同的单元格,那么他可以使用按钮克隆单元格。
此克隆按钮按预期工作,但存在一个相当奇怪的问题。克隆一行后,我注意到当我尝试更改已克隆的任何单元格的内容时,会出现意想不到的结果。例如,如果我将一个单元格的内容更改为 "Ryan",那么其他单元格也可能会突然改变,如果我在更改一个单元格后单击一个单元格,我单击的单元格将自行更改。我很确定这个问题与我真的不知道要修复的克隆方法有关。
我创建了一个可验证的程序,因此您可以自己将其发送到文本中,看看我在说什么。只需使用几次克隆按钮,然后尝试更改单个单元格的内容并观察其他单元格中的结果..
我真的需要解决这个问题,但我不知道该怎么做,非常感谢您的帮助。
主要Class
package jtabletest;
public class JTableTestMain
{
public static void main(String args[]){
JTableTest jTest = new JTableTest();
jTest.createGUI();
}
}
JTableClass
package jtabletest;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class JTableTest
{
protected static DefaultTableModel dtm = new DefaultTableModel();
public static JTable tbl;
public void createGUI(){
final JFrame frame = new JFrame("JTable Test");
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel panelNorth = new JPanel(new BorderLayout());
JPanel panelSouth = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel();
JButton cloneButton = new JButton("Clone");
cloneButton.setPreferredSize(new Dimension(150,40));
buttonPanel.add(cloneButton);
JButton printButton = new JButton("Print");
printButton.setPreferredSize(new Dimension(150,40));
buttonPanel.add(printButton);
tbl = new JTable();
String header[] = new String[]{
"Employee", "Pay-Rate", "Hours Worked"};
dtm.setColumnIdentifiers(header);
tbl.setModel(dtm);
tbl.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for(int i = 0; i < header.length; i++){
tbl.getColumnModel().getColumn(i).setPreferredWidth(200);
}
dtm.addRow(new Object[]{"Pete",".00","40"});
dtm.addRow(new Object[]{"Bob","12.50","42"});
dtm.addRow(new Object[]{"Jamar",".25,25"});
cloneButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
int[] selectedRows = tbl.getSelectedRows();
if(selectedRows.length>0){
@SuppressWarnings("rawtypes")
Vector data = dtm.getDataVector();
int insertPoint = selectedRows[selectedRows.length-1]+1;
for(int i = 0; i < selectedRows.length; i++){
@SuppressWarnings("rawtypes")
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
dtm.insertRow(insertPoint, targetRow);
insertPoint++;
}
dtm.fireTableDataChanged();
}
}
});
printButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
if(null != tbl.getCellEditor()){
tbl.getCellEditor().stopCellEditing();
}
for(int i = 0; i < tbl.getRowCount(); i++){
System.out.println(tbl.getValueAt(i, 0));
System.out.println(tbl.getValueAt(i, 1));
System.out.println(tbl.getValueAt(i, 2));
}
}
});
panelNorth.add(tbl,BorderLayout.NORTH);
panelNorth.setPreferredSize(new Dimension(500,500));
panelSouth.add(buttonPanel,BorderLayout.NORTH);
mainPanel.add(panelNorth,BorderLayout.NORTH);
mainPanel.add(panelSouth,BorderLayout.SOUTH);
frame.add(mainPanel);
frame.setVisible(true);
frame.setSize(1900,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
听起来您是在重复使用相同的引用,而不是在克隆方法上复制新对象。我建议执行以下操作。
1) 首先创建一个新的 Vector,然后看看是否可以解决问题,就像这样。
for(int i = 0; i < selectedRows.length; i++){
@SuppressWarnings("rawtypes")
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
Vector newVector = new Vector();
for (int t = 0; t < targetRow.size(); t++) {
newVector.add(targetRow.get(t));
}
dtm.insertRow(insertPoint, newVector);
insertPoint++;
}
看看这是否能解决您的问题。如果是这样,你就完成了。如果没有那么
2) 创建一个与上面类似的新 Vector,对于 Vector 中任何基于 Class 的对象重新创建它们,因为当前您正在处理指针。
我很难说#1 是否会解决您的问题,因为我不知道来自 table 的 Vector 的内容,如果它是原语,您可能是安全的,否则您可能需要执行解决方案 #2。
您的问题出在这一行:
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
您不是在创建副本,而是在创建新参考,因此当您添加
dtm.insertRow(insertPoint, targetRow)
您要添加的行实际上是相同的,而不是先前选择的行的副本。
你将不得不使用类似
的东西
Vector aux = (Vector)data.elementAt(selectedRows[i]);
Vector targetRow = aux.clone();
使其发挥作用。
克隆是这里的关键词。您没有克隆数据。您只是将引用从一个 Vector 复制到另一个 Vector。因此,由于每一行共享相同的引用,因此值出现在两行中。
因此您需要实际克隆每个元素。
代码如下:
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
Vector clonedRow = new Vector(targetRow.size());
for (Object object: targetRow)
{
clonedRow.addElement( object.clone() );
}
请注意,我以前从未使用过 clone(),因此您可以使用:
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
Vector clonedRow = targetRow.clone();
但我不确定它是否只是克隆 Vector 而不是 Vector 中的元素。
此外,您永远不会调用 firstTableDataChanged() 方法。这是 DefaultTableModle 的工作,它在调用 insertRow(...) 方法时触发适当的方法。
编辑:
是的,使用克隆确实有效,但您需要克隆 Vector 而不是 Vector 中的每个项目:
//dtm.insertRow(insertPoint, targetRow);
dtm.insertRow(insertPoint, (Vector)targetRow.clone());
或
dtm.insertRow(insertPoint, new Vector(targetRow));
我正在做一个工作项目,我遇到了一个不寻常的问题。我有一个 JTable,用户可以用数据填充它。在我的实际代码中,有一个 "add row" 按钮让用户填写一些 GUI,该信息生成行。
此功能的另一个重要功能是用于克隆行的按钮。由于添加行的过程可能非常耗时(有很多字段需要填写),如果用户只需要添加一个新行且有 1 个不同的单元格,那么他可以使用按钮克隆单元格。
此克隆按钮按预期工作,但存在一个相当奇怪的问题。克隆一行后,我注意到当我尝试更改已克隆的任何单元格的内容时,会出现意想不到的结果。例如,如果我将一个单元格的内容更改为 "Ryan",那么其他单元格也可能会突然改变,如果我在更改一个单元格后单击一个单元格,我单击的单元格将自行更改。我很确定这个问题与我真的不知道要修复的克隆方法有关。
我创建了一个可验证的程序,因此您可以自己将其发送到文本中,看看我在说什么。只需使用几次克隆按钮,然后尝试更改单个单元格的内容并观察其他单元格中的结果..
我真的需要解决这个问题,但我不知道该怎么做,非常感谢您的帮助。
主要Class
package jtabletest;
public class JTableTestMain
{
public static void main(String args[]){
JTableTest jTest = new JTableTest();
jTest.createGUI();
}
}
JTableClass
package jtabletest;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class JTableTest
{
protected static DefaultTableModel dtm = new DefaultTableModel();
public static JTable tbl;
public void createGUI(){
final JFrame frame = new JFrame("JTable Test");
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel panelNorth = new JPanel(new BorderLayout());
JPanel panelSouth = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel();
JButton cloneButton = new JButton("Clone");
cloneButton.setPreferredSize(new Dimension(150,40));
buttonPanel.add(cloneButton);
JButton printButton = new JButton("Print");
printButton.setPreferredSize(new Dimension(150,40));
buttonPanel.add(printButton);
tbl = new JTable();
String header[] = new String[]{
"Employee", "Pay-Rate", "Hours Worked"};
dtm.setColumnIdentifiers(header);
tbl.setModel(dtm);
tbl.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for(int i = 0; i < header.length; i++){
tbl.getColumnModel().getColumn(i).setPreferredWidth(200);
}
dtm.addRow(new Object[]{"Pete",".00","40"});
dtm.addRow(new Object[]{"Bob","12.50","42"});
dtm.addRow(new Object[]{"Jamar",".25,25"});
cloneButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
int[] selectedRows = tbl.getSelectedRows();
if(selectedRows.length>0){
@SuppressWarnings("rawtypes")
Vector data = dtm.getDataVector();
int insertPoint = selectedRows[selectedRows.length-1]+1;
for(int i = 0; i < selectedRows.length; i++){
@SuppressWarnings("rawtypes")
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
dtm.insertRow(insertPoint, targetRow);
insertPoint++;
}
dtm.fireTableDataChanged();
}
}
});
printButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
if(null != tbl.getCellEditor()){
tbl.getCellEditor().stopCellEditing();
}
for(int i = 0; i < tbl.getRowCount(); i++){
System.out.println(tbl.getValueAt(i, 0));
System.out.println(tbl.getValueAt(i, 1));
System.out.println(tbl.getValueAt(i, 2));
}
}
});
panelNorth.add(tbl,BorderLayout.NORTH);
panelNorth.setPreferredSize(new Dimension(500,500));
panelSouth.add(buttonPanel,BorderLayout.NORTH);
mainPanel.add(panelNorth,BorderLayout.NORTH);
mainPanel.add(panelSouth,BorderLayout.SOUTH);
frame.add(mainPanel);
frame.setVisible(true);
frame.setSize(1900,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
听起来您是在重复使用相同的引用,而不是在克隆方法上复制新对象。我建议执行以下操作。
1) 首先创建一个新的 Vector,然后看看是否可以解决问题,就像这样。
for(int i = 0; i < selectedRows.length; i++){
@SuppressWarnings("rawtypes")
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
Vector newVector = new Vector();
for (int t = 0; t < targetRow.size(); t++) {
newVector.add(targetRow.get(t));
}
dtm.insertRow(insertPoint, newVector);
insertPoint++;
}
看看这是否能解决您的问题。如果是这样,你就完成了。如果没有那么
2) 创建一个与上面类似的新 Vector,对于 Vector 中任何基于 Class 的对象重新创建它们,因为当前您正在处理指针。
我很难说#1 是否会解决您的问题,因为我不知道来自 table 的 Vector 的内容,如果它是原语,您可能是安全的,否则您可能需要执行解决方案 #2。
您的问题出在这一行:
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
您不是在创建副本,而是在创建新参考,因此当您添加
dtm.insertRow(insertPoint, targetRow)
您要添加的行实际上是相同的,而不是先前选择的行的副本。
你将不得不使用类似
的东西Vector aux = (Vector)data.elementAt(selectedRows[i]);
Vector targetRow = aux.clone();
使其发挥作用。
克隆是这里的关键词。您没有克隆数据。您只是将引用从一个 Vector 复制到另一个 Vector。因此,由于每一行共享相同的引用,因此值出现在两行中。
因此您需要实际克隆每个元素。
代码如下:
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
Vector clonedRow = new Vector(targetRow.size());
for (Object object: targetRow)
{
clonedRow.addElement( object.clone() );
}
请注意,我以前从未使用过 clone(),因此您可以使用:
Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
Vector clonedRow = targetRow.clone();
但我不确定它是否只是克隆 Vector 而不是 Vector 中的元素。
此外,您永远不会调用 firstTableDataChanged() 方法。这是 DefaultTableModle 的工作,它在调用 insertRow(...) 方法时触发适当的方法。
编辑:
是的,使用克隆确实有效,但您需要克隆 Vector 而不是 Vector 中的每个项目:
//dtm.insertRow(insertPoint, targetRow);
dtm.insertRow(insertPoint, (Vector)targetRow.clone());
或
dtm.insertRow(insertPoint, new Vector(targetRow));