Java 在JTextField和setText中撤消
JTextField具有现成的撤销支持。它对用户交互很有效,但不幸的是,如果调用方法setText(stringstr),将导致两次而不是一次可撤消的编辑。因此,此代码看起来和感觉都很好,但不起作用:Java 在JTextField和setText中撤消,java,swing,jtextfield,settext,undo-redo,Java,Swing,Jtextfield,Settext,Undo Redo,JTextField具有现成的撤销支持。它对用户交互很有效,但不幸的是,如果调用方法setText(stringstr),将导致两次而不是一次可撤消的编辑。因此,此代码看起来和感觉都很好,但不起作用: UndoManager undoManager = new UndoManager(); JTextField tf = new JTextField(); tf.setText("initial value"); tf.getDocument().addUndoableEditListener(
UndoManager undoManager = new UndoManager();
JTextField tf = new JTextField();
tf.setText("initial value");
tf.getDocument().addUndoableEditListener(undoManager);
tf.setText("new value");
undoManager.undo();
System.out.println(tf.getText()); // Prints empty string
undoManager.undo();
System.out.println(tf.getText()); // Prints "initial value" as expected
JTextField是否可以将setText()作为唯一一个可撤消的编辑处理?不支持
dragandrop
和UndoAndRedo
必须将数据加载到UndoManager
并定义UndoAction UndoAction=new UndoAction()代码>对于简单的图形
或e.i
class UndoHandler implements UndoableEditListener {
@Override
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
undoAction.update();
}
}
并创建Swing动作
(添加到JButton)
以将内容刷新回JTextField
class UndoAction extends AbstractAction {
private static final long serialVersionUID = 1L;
UndoAction() {
super("Undo");
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
try {
undo.undo();
} catch (CannotUndoException ex) {
System.out.println("Unable to undo: " + ex);
ex.printStackTrace();
}
update();
}
protected void update() {
if (undo.canUndo()) {
setEnabled(true);
putValue(Action.NAME, undo.getUndoPresentationName());
} else {
setEnabled(false);
putValue(Action.NAME, "Undo");
}
}
}
找到了一个解决方法:
public class SetTextEditUndo extends AbstractUndoableEdit {
public JTextField src;
public String oldValue;
public String newValue;
public SetTextEditUndo(JTextField src, String oldValue, String newValue) {
this.src = src;
this.oldValue = oldValue;
this.newValue = newValue;
}
@Override
public void undo() throws CannotUndoException {
setTextIgnoringUndo(src, oldValue);
}
@Override
public void redo() throws CannotRedoException {
setTextIgnoringUndo(src, newValue);
}
@Override
public boolean canUndo() {
return true;
}
@Override
public boolean canRedo() {
return true;
}
public static void setTextIgnoringUndo(JTextField tf, String str) {
PlainDocument doc = (PlainDocument) tf.getDocument();
UndoableEditListener uel = doc.getUndoableEditListeners()[0];
doc.removeUndoableEditListener(uel);
tf.setText(str);
doc.addUndoableEditListener(uel);
}
}
另一个选项是覆盖文档#替换(…)
通过编程设置文本后,似乎是调用的好时机。@Andrew Thompson不,实际上我有一个带有一些文本操作的弹出菜单,如大写、反转字符串等。我希望这些操作可以在一次操作中撤消,否则用户一次单击就将字符串大写是不现实的,但是需要进行两次撤销才能去除资本。我并没有真正了解您的代码,但您的意思是在重载的撤销/重做方法中扩展UndoableEdit和make setText吗?
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.undo.*;
import javax.swing.event.*;
public class ReplaceUndoableEditDemo {
private final UndoManager um = new UndoManager();
private final JTextField tf = new JTextField(24);
private final UndoManager undoManager = new UndoManager();
private final JTextField field = new JTextField(24);
private final Document doc = new PlainDocument() {
@Override public void replace(
int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
undoManager.undoableEditHappened(new UndoableEditEvent(
this, new ReplaceUndoableEdit(offset, length, text)));
replaceIgnoringUndo(offset, length, text, attrs);
}
private void replaceIgnoringUndo(
int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
for(UndoableEditListener uel: getUndoableEditListeners()) {
removeUndoableEditListener(uel);
}
super.replace(offset, length, text, attrs);
for(UndoableEditListener uel: getUndoableEditListeners()) {
addUndoableEditListener(uel);
}
}
class ReplaceUndoableEdit extends AbstractUndoableEdit {
private final String oldValue;
private final String newValue;
private int offset;
public ReplaceUndoableEdit(int offset, int length, String newValue) {
String txt;
try {
txt = getText(offset, length);
} catch(BadLocationException e) {
txt = null;
}
this.oldValue = txt;
this.newValue = newValue;
this.offset = offset;
}
@Override public void undo() throws CannotUndoException {
try {
replaceIgnoringUndo(offset, newValue.length(), oldValue, null);
} catch(BadLocationException ex) {
throw new CannotUndoException();
}
}
@Override public void redo() throws CannotRedoException {
try {
replaceIgnoringUndo(offset, oldValue.length(), newValue, null);
} catch(BadLocationException ex) {
throw new CannotUndoException();
}
}
@Override public boolean canUndo() {
return true;
}
@Override public boolean canRedo() {
return true;
}
}
};
public JComponent makeUI() {
tf.getDocument().addUndoableEditListener(um);
doc.addUndoableEditListener(undoManager);
field.setDocument(doc);
field.setText("aaaaaaaaa");
tf.setText("default");
JPanel p = new JPanel();
p.add(tf);
p.add(field);
p.add(new JButton(new AbstractAction("undo") {
@Override public void actionPerformed(ActionEvent e) {
try {
undoManager.undo();
um.undo();
} catch(Exception ex) {
java.awt.Toolkit.getDefaultToolkit().beep();
}
}
}));
p.add(new JButton(new AbstractAction("redo") {
@Override public void actionPerformed(ActionEvent e) {
try {
undoManager.redo();
um.redo();
} catch(Exception ex) {
java.awt.Toolkit.getDefaultToolkit().beep();
}
}
}));
p.add(new JButton(new AbstractAction("setText") {
@Override public void actionPerformed(ActionEvent e) {
String str = new Date().toString();
tf.setText(str);
field.setText(str);
}
}));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ReplaceUndoableEditDemo().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}