简体   繁体   中英

Editing JTable cells after being cloned

I'm working on a project for work and I have reached an unusual problem. I have a JTable that the user can populate with data. In my actual code there is an "add row" button that lets the user fill out some GUI's and that information generates the row.

Another important feature of this is a button to clone rows. Since the process of adding a row can be very time consuming (there are many fields to fill out) if a user only needs to add a new row with 1 cell different then he can clone the cell using the button.

This clone button works as expected however there is a rather odd problem. Once a row has been cloned I noticed that when I attempt to change the contents of any cells that have been cloned there are unexpected results. For example if I change a cell's contents to "Ryan" then other cells may also suddenly change and if I even click on a cell after changing one the cell I click on will change by itself. I'm quite sure that this problem is related to the clone method I just really have no idea to fix.

I created a verifiable program so you can text it out for yourself and see what I'm talking about. Just use the clone button a few times and then try changing the contents of individual cells and watch the results in the other cells..

I really need to fix this but I'm lost on what to do, and help is GREATLY appreciated.

Main Class

package jtabletest;

public class JTableTestMain 

{

    public static void main(String args[]){

        JTableTest jTest = new JTableTest();
        jTest.createGUI();

    }

}

JTable Class

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","$10.00","40"});
        dtm.addRow(new Object[]{"Bob","12.50","42"});
        dtm.addRow(new Object[]{"Jamar","$7.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);



    }

}

It sounds like you are reusing the same reference rather than copying new objects on the clone method. I would suggest doing the following.

1) First create a new Vector and see if that will do the trick, like so.

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++;
}

and see if this will resolve your problem. If it does you are done. If it doesn't then

2) Create a new Vector similar to above, for any Class based object in the Vector recreate them as currently you are dealing with pointers.

It's a bit hard for me to say if #1 will fix your problem as I don't know the contents of the Vector coming from the table, if it is primitives you are probably safe otherwise you may need to do solution #2.

Your problem is in this line:

Vector targetRow = (Vector)data.elementAt(selectedRows[i]);

you are not creating a copy, you are creating a new reference so when you add

dtm.insertRow(insertPoint, targetRow)

the row you are adding is actually the same, not a copy of the previosly selected row.

You will have to use something like

Vector aux = (Vector)data.elementAt(selectedRows[i]);
Vector targetRow = aux.clone();

to make it work.

Clone is the keyword here. You are not cloning the data. You are just copying the references from one Vector to another. So since each row shares the same references the value appears in both rows.

So you need to actually clone each element.

The code would be something like:

  Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
  Vector clonedRow = new Vector(targetRow.size());

  for (Object object: targetRow)
  {
     clonedRow.addElement( object.clone() );
  }

Note, I've never used clone() before so you might be able to use:

  Vector targetRow = (Vector)data.elementAt(selectedRows[i]);
  Vector clonedRow = targetRow.clone();

but I'm not sure if it just clones the Vector and not the elements in the Vector.

Also, you would never invoke the firstTableDataChanged() method. That is the job of the DefaultTableModle to fire the appropriate method when the insertRow(...) method is invoked.

Edit:

Yes, using the clone does work but you need to clone the Vector not each item in the Vector:

//dtm.insertRow(insertPoint, targetRow);
dtm.insertRow(insertPoint, (Vector)targetRow.clone());

or

dtm.insertRow(insertPoint, new Vector(targetRow));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM