gpt4 book ai didi

java - 如何使 Java jtable、单元格编辑器和撤消协同工作而不创建无关的撤消事件?

转载 作者:行者123 更新时间:2023-12-01 10:53:29 25 4
gpt4 key购买 nike

在以下代码中,我为第一列创建一个带有自定义单元格编辑器的 jtable,然后向该表添加撤消功能。当您运行该程序时,该程序允许您更改第一列中的值(通过向已有的“abc”附加“d”然后附加“e”进行测试)。现在输入control-z(撤消)并再次输入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));
}

}

最佳答案

当你按下一个键时,会发生一系列的事情。 JTable 处理击键,它检查单元格是否可编辑(如 TableModel),然后向编辑器询问当前选定的单元格是否发生事件应该编辑单元格 (CellEditor#isCellEditable(EventObject))。

如果此方法返回true,则编辑器已准备就绪,TableModel 中的值将应用于编辑器(即调用 setText ),并且编辑器被添加到 JTable 中,最后,触发编辑模式的事件被重新分派(dispatch)到编辑器,在您的情况下为 Ctrl+Z,然后触发并撤消事件,返回编辑器的初始状态(在调用 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;
}

};

防止在发生特定击键时将 JTable 置于编辑状态

已更新

基于安德鲁的回答 JTextArea setText() & UndoManager ,我设计了一个“可配置”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 来支持此功能,例如...

public static class MyCellEditor extends AbstractCellEditor implements TableCellEditor {

private JTextField editor;
private UndoableEditHandler undoableEditHandler;

public MyCellEditor(JTextField editor) {
this.editor = editor;
undoableEditHandler = new UndoableEditHandler(editor);
editor.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
}

@Override
public Object getCellEditorValue() {
return editor.getText();
}

@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
undoableEditHandler.setCanUndo(false);
editor.setText(value == null ? null : value.toString());
undoableEditHandler.setCanUndo(true);
return editor;
}

}

关于java - 如何使 Java jtable、单元格编辑器和撤消协同工作而不创建无关的撤消事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33728104/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com