在JTable中撤消

时间:2014-04-09 11:16:57

标签: java swing jtable undo-redo defaulttablemodel

看看下面显示的代码。当我向表中添加两行,然后尝试执行撤消操作时,我得到一个java.lang.ArrayIndexOutOfBoundsException: 11 >= 11。任何人都可以告诉我代码有什么问题吗?

import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.text.TabExpander;
import javax.swing.undo.*;


public class UndoTable
{
    public static void main(String[] args)
    {
        Object data[][] = {
            {"AMZN", "Amazon", 41.28, "BUY"},
            {"EBAY", "eBay", 41.57, "BUY"},
            {"GOOG", "Google", 388.33, "SELL"},
            {"MSFT", "Microsoft", 26.56, "SELL"},
            {"NOK", "Nokia Corp", 17.13, "BUY"},
            {"ORCL", "Oracle Corp.", 12.52, "BUY"},
            {"SUNW", "Sun Microsystems", 3.86, "BUY"},
            {"TWX",  "Time Warner", 17.66, "SELL"},
            {"VOD",  "Vodafone Group", 26.02, "SELL"},
            {"YHOO", "Yahoo!", 37.69, "BUY"}
        };
        String columns[] = {"Symbol", "Name", "Price", "Guidance"};

        final JvUndoableTableModel tableModel = new JvUndoableTableModel(data, columns);
       final JTable table = new JTable(tableModel);
        JScrollPane pane = new JScrollPane(table);

        JvUndoManager undoManager = new JvUndoManager();
        tableModel.addUndoableEditListener(undoManager);

        JMenu editMenu = new JMenu("Edit");

        Action addrowaction = new AbstractAction("Add Row") {
            private static final long serialVersionUID = 1433684360133156145L;


            public void actionPerformed(ActionEvent e) {
                tableModel.insertRow(table.getRowCount(), new Object[]{"YHOO", "Yahoo!", 37.69, "BUY"});


            }
        };
        editMenu.add(undoManager.getUndoAction());
        //editMenu.add(undoManager.getRedoAction());

        JMenuBar menuBar = new JMenuBar();
        menuBar.add(editMenu);
        editMenu.add(addrowaction);


        JFrame frame = new JFrame("Undoable JTable");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setJMenuBar(menuBar);
        frame.add(pane, BorderLayout.CENTER);
        frame.setSize(300, 150);
        frame.setLocation(200, 300);
        frame.setVisible(true);
    }
}


class JvUndoableTableModel extends DefaultTableModel
{
    public JvUndoableTableModel(Object[][] data, Object[] columnNames)
    {
        super(data, columnNames);
    }


    public Class getColumnClass(int column)
    {
        if (column >= 0 && column < getColumnCount())
            return getValueAt(0, column).getClass();

        return Object.class;
    }



    @Override
    public void setValueAt(Object value, int row, int column)
    {
        setValueAt(value, row, column, true);
    }


    public void setValueAt(Object value, int row, int column, boolean undoable)
    {
        UndoableEditListener listeners[] = getListeners(UndoableEditListener.class);
        if (undoable == false || listeners == null)
        {
            super.setValueAt(value, row, column);
            return;
        }


        Object oldValue = getValueAt(row, column);
        super.setValueAt(value, row, column);

        JvCellEdit cellEdit = new JvCellEdit(this, oldValue, value, row, column);
        UndoableEditEvent editEvent = new UndoableEditEvent(this, cellEdit);
        for (UndoableEditListener listener : listeners)
            listener.undoableEditHappened(editEvent);

    }

    //adding new cell to the table
    public void insertRow(int row, Object[] rowData){
        insertRow(row, rowData, true);
    }

    public void insertRow(int row,
            Object[] rowData,boolean undoable){
        UndoableEditListener listeners[] = getListeners(UndoableEditListener.class);
        if (undoable == false || listeners == null)
        {
            super.insertRow(row, rowData);
            return;
        }

        super.insertRow(row, rowData);
        JvCellNew cellNew = new JvCellNew(this, rowData, row);

        UndoableEditEvent editEvent = new UndoableEditEvent(this, cellNew);
        for (UndoableEditListener listener : listeners)
            listener.undoableEditHappened(editEvent);

    }


    //removing row from the table
    public void removeRow(int row){
        removeRow(row, true);
    }
    public void removeRow(int row, boolean undoable){
        UndoableEditListener listeners[] = getListeners(UndoableEditListener.class);
        if (undoable == false || listeners == null)
        {
            super.removeRow(row);
            return;
        }
        super.removeRow(row);
        JvCellNew cellNew = new JvCellNew(this, row);
        UndoableEditEvent editEvent = new UndoableEditEvent(this, cellNew);
        for (UndoableEditListener listener : listeners)
            listener.undoableEditHappened(editEvent);

    }


    public void addUndoableEditListener(UndoableEditListener listener)
    {
        listenerList.add(UndoableEditListener.class, listener);
    }
}


class JvCellEdit extends AbstractUndoableEdit
{
    protected JvUndoableTableModel tableModel;
    protected Object oldValue;
    protected Object newValue;
    protected int row;
    protected int column;


    public JvCellEdit(JvUndoableTableModel tableModel, Object oldValue, Object newValue, int row, int column)
    {
        this.tableModel = tableModel;
        this.oldValue = oldValue;
        this.newValue = newValue;
        this.row = row;
        this.column = column;
    }


    @Override
    public String getPresentationName()
    {
        return "Cell Edit";
    }


    @Override
    public void undo() throws CannotUndoException
    {
        super.undo();

        tableModel.setValueAt(oldValue, row, column, false);
    }
}
class JvCellNew extends AbstractUndoableEdit
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    protected JvUndoableTableModel tableModel;
    protected Object[] rowData;
    protected int row;

    public JvCellNew(JvUndoableTableModel tableModel, Object[] rowData, int row)
    {
        this.tableModel = tableModel;        
        this.rowData = rowData;
        this.row = row;

    }
    public JvCellNew(JvUndoableTableModel tableModel, int row)
    {
        this.tableModel = tableModel;        
        this.row = row;

    }
    @Override
    public String getPresentationName()
    {
        return "Cell New";
    }
    public void undo() throws CannotUndoException
    {
        super.undo();
        tableModel.removeRow(row);

    }
}


class JvUndoManager extends UndoManager
{
    protected Action undoAction;
   // protected Action redoAction;


    public JvUndoManager()
    {
        this.undoAction = new JvUndoAction(this);
        synchronizeActions();           // to set initial names
    }


    public Action getUndoAction()
    {
        return undoAction;
    }



    @Override
    public boolean addEdit(UndoableEdit anEdit)
    {
        try
        {
            return super.addEdit(anEdit);
        }
        finally
        {
            synchronizeActions();
        }
    }


    @Override
    protected void undoTo(UndoableEdit edit) throws CannotUndoException
    {
        try
        {
            super.undoTo(edit);
        }
        finally
        {
            synchronizeActions();
        }
    }


    protected void synchronizeActions()
    {
        undoAction.setEnabled(canUndo());
        undoAction.putValue(Action.NAME, getUndoPresentationName());
    }
}


class JvUndoAction extends AbstractAction
{
    protected final UndoManager manager;


    public JvUndoAction(UndoManager manager)
    {
        this.manager = manager;
    }


    public void actionPerformed(ActionEvent e)
    {
        try
        {
            manager.undo();
        }
        catch (CannotUndoException ex)
        {
            ex.printStackTrace();
        }
    }
}

线程中的异常&#34; AWT-EventQueue-0&#34; java.lang.ArrayIndexOutOfBoundsException:11&gt; = 11     at java.util.Vector.removeElementAt(Unknown Source)     在javax.swing.table.DefaultTableModel.removeRow(未知来源)     在JvUndoableTableModel.removeRow(UndoTable.java:151)     在JvUndoableTableModel.removeRow(UndoTable.java:142)     在JvCellNew.undo(UndoTable.java:233)     在javax.swing.undo.UndoManager.undoTo(未知来源)     在JvUndoManager.undoTo(UndoTable.java:279)     在javax.swing.undo.UndoManager.undo(未知来源)     在JvUndoAction.actionPerformed(UndoTable.java:311)     在javax.swing.AbstractButton.fireActionPerformed(未知来源)     at javax.swing.AbstractButton $ Handler.actionPerformed(Unknown Source)     在javax.swing.DefaultButtonModel.fireActionPerformed(未知来源)     在javax.swing.DefaultButtonModel.setPressed(未知来源)     在javax.swing.AbstractButton.doClick(未知来源)     在javax.swing.plaf.basic.BasicMenuItemUI.doClick(未知来源)     at javax.swing.plaf.basic.BasicMenuItemUI $ Handler.mouseReleased(Unknown Source)     at java.awt.Component.processMouseEvent(Unknown Source)     在javax.swing.JComponent.processMouseEvent(未知来源)     at java.awt.Component.processEvent(Unknown Source)     at java.awt.Container.processEvent(Unknown Source)     at java.awt.Component.dispatchEventImpl(Unknown Source)     at java.awt.Container.dispatchEventImpl(Unknown Source)     at java.awt.Component.dispatchEvent(Unknown Source)     at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)     at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)     at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)     at java.awt.Container.dispatchEventImpl(Unknown Source)     at java.awt.Window.dispatchEventImpl(Unknown Source)     at java.awt.Component.dispatchEvent(Unknown Source)     at java.awt.EventQueue.dispatchEventImpl(Unknown Source)     在java.awt.EventQueue.access $ 200(未知来源)     在java.awt.EventQueue $ 3.run(未知来源)     在java.awt.EventQueue $ 3.run(未知来源)     at java.security.AccessController.doPrivileged(Native Method)     at java.security.ProtectionDomain $ 1.doIntersectionPrivilege(Unknown Source)     at java.security.ProtectionDomain $ 1.doIntersectionPrivilege(Unknown Source)     在java.awt.EventQueue $ 4.run(未知来源)     在java.awt.EventQueue $ 4.run(未知来源)     at java.security.AccessController.doPrivileged(Native Method)     at java.security.ProtectionDomain $ 1.doIntersectionPrivilege(Unknown Source)     at java.awt.EventQueue.dispatchEvent(Unknown Source)     at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)     at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)     at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)     at java.awt.EventDispatchThread.pumpEvents(Unknown Source)     at java.awt.EventDispatchThread.pumpEvents(Unknown Source)     在java.awt.EventDispatchThread.run(未知来源)

1 个答案:

答案 0 :(得分:2)

问题似乎与以下事实有关:执行撤消会导致再次存储为可撤消操作的操作。 (由于删除行创建了一个新的JvCellNew这一事实,这一点尤其令人困惑,根据名称,它可能表明已添加了新行。

&#34;症状&#34;可以通过指定撤消的操作可能不会再次撤消来解决:

class JvCellNew
{
    ....
    public void undo() throws CannotUndoException
    {
        super.undo();

        // Pass in "false" as the second argument here, to indicate
        // that this row removal should NOT cause an undoable edit
        tableModel.removeRow(row, false);
    }
}

打印一些调试信息时可以更容易地找到这样的东西,例如:与

class JvCellNew extends AbstractUndoableEdit
{
    ....
    @Override
    public String getPresentationName()
    {
        return "Cell New "+row;  // Print the row number
    }

    // Provide a useful toString implementation
    @Override
    public String toString()
    {
        return getPresentationName();
    }
}


class JvUndoManager extends UndoManager
{
    ....

    @Override
    public boolean addEdit(UndoableEdit anEdit)
    {
        try
        {
            boolean b = super.addEdit(anEdit);

            // Print the current state of this manager 
            System.out.println("After adding "+anEdit);
            for (UndoableEdit e : this.edits)
            {
                System.out.println(e);
            }
            return b;

        }
        finally
        {
            synchronizeActions();
        }


    }


    @Override
    protected void undoTo(UndoableEdit edit) throws CannotUndoException
    {
        try
        {
            super.undoTo(edit);

            // Print the current state of this manager 
            System.out.println("After undo to "+edit);
            for (UndoableEdit e : this.edits)
            {
                System.out.println(e);
            }

        }
        finally
        {
            synchronizeActions();
        }
    }
}

此外,您至少应考虑区分描述添加行的可撤消编辑和描述删除行的可撤消编辑。