如何创建Java jtables,单元格编辑器和撤消协同工作,而不创建出色的撤消事件?

时间:2015-11-16 03:33:03

标签: java swing jtable undo tablecelleditor

在下面的代码中,我使用第一列的自定义单元格编辑器创建一个jtable,然后向表中添加撤消功能。当你运行程序时,程序允许你改变第一列中的值(通过附加" d"然后" e"来测试" abc&# 34;已经存在)。现在输入control-z(undo)并再次输入control-z。它按预期工作。但现在再次进入control-z(撤消)。这次" abc"被删除了。看起来swing系统正在设置列的初始值并为该操作创建撤消事件,然后用户可以撤消该操作。我的问题 - 我如何编写代码以便用户只能撤消用户的操作?

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.DefaultCellEditor;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;

public class UndoExample extends JFrame {

private static final long serialVersionUID = 1L;;   
static Boolean objEnableUndoRedoActions = true; 
UndoExample rootFrame;

public UndoExample() {
    // This procedure starts the whole thing off.

    //Create table
    final String[] tableColumns = {"Column 1", "Column 2"};
    JTable tabUndoExample = new JTable(
            new DefaultTableModel(null, tableColumns) {
                private static final long serialVersionUID = 1L;                
    });
    final DefaultTableModel tabUndoExampleModel = (DefaultTableModel) tabUndoExample
            .getModel();
    tabUndoExampleModel.addRow(new Object[]{"abc", true});
    tabUndoExampleModel.addRow(new Object[]{"zyw", false});

    // Create the undo/redo manager
    UndoManager objUndoManager = new UndoManager();

    // Create a cell editor
    JTextField tfTabField = new JTextField();
    TableCellEditor objEditor = new DefaultCellEditor(tfTabField);

    // Make the cell editor the default editor for this table's first column
    tabUndoExample.getColumnModel().getColumn(0)
        .setCellEditor(objEditor);

    // Create the undo action on the field's document for the column
    tfTabField.getDocument().addUndoableEditListener(
            new uelUndoRedoTableCellField(objUndoManager, tabUndoExample));

    // Allow undo and redo to be entered by the user
    UndoRedoSetKeys(this, "Example", objUndoManager);
    tabUndoExample.setInheritsPopupMenu(true);

     //Add the table to the frame and show the frame         
    this.add(tabUndoExample);
    this.pack();
    setLocationRelativeTo(null);
    }

public static void main(final String[] args) {
    // Launches the application. This is required syntax.

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {       
                final UndoExample rootFrame = new UndoExample();
                rootFrame.setVisible(true);             
            } catch (final Exception e) {

            }
        }
    });
}

@SuppressWarnings("serial")
static class aueUndoRedoTableCellField extends AbstractUndoableEdit {
    // Wrap the text edit action item as we need to add the table
    // row and column information.  This code is invoked when the
    // code sees an undo event created and then later when the 
    // user requests an undo/redo.

    JTable objTable = null;
    UndoableEdit objUndoableEdit;
    int objCol = -1;
    int objRow = -1;

    public aueUndoRedoTableCellField(UndoableEdit undoableEdit,
            JTable table, int row, int col) {
        super();
        objUndoableEdit = undoableEdit;
        objTable = table;
        objCol = col;
        objRow = row;
    }

    public void redo() throws CannotRedoException {
        // When the user enters redo (or undo), this code sets
        // that we are doing an redo (or undo), sets the cursor
        // to the right location, and then does the undo (or redo)
        // to the table cell.  
        UndoRedoManagerSetEnabled(false);
        super.redo();
        @SuppressWarnings("unused")
        boolean success = objTable.editCellAt(objRow, objCol);
        objTable.changeSelection(objRow, objCol, false, false);
        objUndoableEdit.redo();
        UndoRedoManagerSetEnabled(true);
    }

    public void undo() throws CannotUndoException {
        super.undo();
        UndoRedoManagerSetEnabled(false);
        @SuppressWarnings("unused")
        boolean success = objTable.editCellAt(objRow, objCol);
        objTable.changeSelection(objRow, objCol, false, false);
        objUndoableEdit.undo();
        UndoRedoManagerSetEnabled(true);
    }
}

static class aUndoRedo extends AbstractAction {
    // This code is bound to the undo/redo keystrokes and tells
    // Java what commands to run when the keys are later entered
    // by the user.

    private static final long serialVersionUID = 1L;
    Boolean objUndo = true;
    UndoManager objUndoManager = null;
    final String objLocation;

    public aUndoRedo(Boolean Undo, UndoManager undoManager, String location) {
        super();
        objUndo = Undo;
        objUndoManager = undoManager;
        objLocation = location;
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        try {
            // See if operation allowed
            if (!objUndoManager.canUndo() && objUndo
                    || !objUndoManager.canRedo() && !objUndo)
                return;
            UndoRedoManagerSetEnabled(false);
            if (objUndo) {
                objUndoManager.undo();
            } else {
                objUndoManager.redo();
            }
            UndoRedoManagerSetEnabled(true);
            // Catch errors and let user know
        } catch (Exception e) {

            UndoRedoManagerSetEnabled(true);
        }
    }
}

static class uelUndoRedoTableCellField implements UndoableEditListener {
    // This action is called when the user changes the table's
    // text cell.  It saves the change for later undo/redo.

    private UndoManager objUndoManager = null;
    private JTable objTable = null;

    public uelUndoRedoTableCellField(UndoManager undoManager,
            JTable table) {
        objUndoManager = undoManager;
        objTable = table;
    }

    @Override
    public void undoableEditHappened(UndoableEditEvent e) {
        // Remember the edit but only if the code isn't doing
        // an undo or redo currently.
        if (UndoRedoManagerIsEnabled()) {
            objUndoManager.addEdit(new aueUndoRedoTableCellField(e
                    .getEdit(), objTable, objTable.getSelectedRow(),
                    objTable.getSelectedColumn()));
        }
    }
}

static public Boolean UndoRedoManagerIsEnabled() {
    // See if we are currently doing an undo/redo.
    // Return true if so.
    return objEnableUndoRedoActions;
}

static public void UndoRedoManagerSetEnabled(Boolean state) {
    // Set the state of whether we are in undo/redo code.
    objEnableUndoRedoActions = state;
}


static void UndoRedoSetKeys(JFrame frame, final String location, UndoManager undoManager) {
    // Allow undo and redo to be called via these keystrokes for this dialog
    final String cntl_y = "CNTL_Y";
    final KeyStroke ksCntlY = KeyStroke.getKeyStroke("control Y");
    final String cntl_z = "CNTL_Z";
    final KeyStroke ksCntlZ = KeyStroke.getKeyStroke("control Z");

    JRootPane root = frame.getRootPane();
    root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(ksCntlZ, cntl_z);
    root.getActionMap().put(
            cntl_z,
            new aUndoRedo(true, undoManager, location));
    root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
            .put(ksCntlY, cntl_y);
    root.getActionMap().put(
            cntl_y,
            new aUndoRedo(false, undoManager, location));
}

}

1 个答案:

答案 0 :(得分:1)

当您按某个键时,会发生一系列事情。 JTable处理键击,它检查单元格是否可编辑(如TableModel),然后如果事件应该编辑单元格,它会询问编辑器当前选定的单元格({ {1}})。

如果此方法返回CellEditor#isCellEditable(EventObject),则编辑器已准备好,true的值将应用于编辑器(即调用TableModel),并将编辑器添加到setText,最后,触发编辑模式的事件被重新调度到编辑器,在你的情况下是 Ctrl + Z ,然后触发并撤消事件,返回编辑器的初始状态(在JTable被调用之前)。

您可以尝试使用类似......

的内容
setText

以防止在发生特定键击时将TableCellEditor objEditor = new DefaultCellEditor(tfTabField) { @Override public boolean isCellEditable(EventObject anEvent) { boolean isEditable = super.isCellEditable(anEvent); //To change body of generated methods, choose Tools | Templates. if (isEditable && anEvent instanceof KeyEvent) { KeyEvent ke = (KeyEvent) anEvent; if (ke.isControlDown() && ke.getKeyCode() == KeyEvent.VK_Z) { isEditable = false; } } return isEditable; } }; 置于编辑状态

<强>更新

基于Andrew JTextArea setText() & UndoManager的答案,我设计了一个可配置的&#34; JTable可以设置为忽略可撤消的操作,例如......

UndoableEditListener

现在,您将拥有自己的设备import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.UndoableEditEvent; import javax.swing.event.UndoableEditListener; import javax.swing.text.Document; import javax.swing.text.PlainDocument; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; public class FixedField { public static void main(String[] args) { new FixedField(); } public FixedField() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public static class UndoableEditHandler implements UndoableEditListener { private static final int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); private UndoManager undoManager = new UndoManager(); private boolean canUndo = true; public UndoableEditHandler(JTextField field) { Document doc = field.getDocument(); doc.addUndoableEditListener(this); field.getActionMap().put("Undo", new AbstractAction("Undo") { @Override public void actionPerformed(ActionEvent evt) { try { if (undoManager.canUndo()) { undoManager.undo(); } } catch (CannotUndoException e) { System.out.println(e); } } }); field.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, MASK), "Undo"); field.getActionMap().put("Redo", new AbstractAction("Redo") { @Override public void actionPerformed(ActionEvent evt) { try { if (undoManager.canRedo()) { undoManager.redo(); } } catch (CannotRedoException e) { System.out.println(e); } } }); field.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, MASK), "Redo"); } @Override public void undoableEditHappened(UndoableEditEvent e) { if (canUndo()) { undoManager.addEdit(e.getEdit()); } } public void setCanUndo(boolean canUndo) { this.canUndo = canUndo; } public boolean canUndo() { return canUndo; } } public class TestPane extends JPanel { public TestPane() { JTextField field = new JTextField(10); UndoableEditHandler handler = new UndoableEditHandler(field); handler.setCanUndo(false); field.setText("Help"); handler.setCanUndo(true); add(field); } } } 以支持此功能,例如......

TableCellEditor