Java 使用单个JTextArea和多个管理器
我有一个Java 使用单个JTextArea和多个管理器,java,swing,jtextarea,undo,redo,Java,Swing,Jtextarea,Undo,Redo,我有一个JTextArea和一个JComboBox来允许我循环浏览各种打开的文件-当我选择不同的文件时,JTextArea的内容会发生变化。我试图为每个文件维护一个不同的撤消缓冲区,并为每个文件定义了一个单独的UndoManager 我创建了一个更简单的SSCCE来演示我的问题,它使用了两个缓冲区,我称之为“一”和“二”——通过一个简单的按钮在它们之间切换。一旦发生undoabledit,它将检查活动缓冲区并在相应的undoabledit管理器上执行addEdit()。按下“撤消”按钮后,它将检
JTextArea
和一个JComboBox
来允许我循环浏览各种打开的文件-当我选择不同的文件时,JTextArea
的内容会发生变化。我试图为每个文件维护一个不同的撤消缓冲区,并为每个文件定义了一个单独的UndoManager
我创建了一个更简单的SSCCE来演示我的问题,它使用了两个缓冲区,我称之为“一”和“二”——通过一个简单的按钮在它们之间切换。一旦发生undoabledit
,它将检查活动缓冲区并在相应的undoabledit管理器上执行addEdit()
。按下“撤消”按钮后,它将检查canUndo()
,并在相应的UndoManager
上执行Undo()
。我有一个名为ignoreEdit
的标志,在缓冲区之间切换时使用该标志来忽略记录的编辑
如果我从不在缓冲区之间切换,那么我就不会有问题,撤消会按预期工作。只有当我在缓冲区之间切换并似乎“破坏”文档时,它才会失败。可以使用以下步骤重新创建问题:
在缓冲区“一”中,键入:
切换到缓冲区“2”,类型:
切换到缓冲区“一”并多次按下“撤消”按钮。经过几次撤消操作后,缓冲区看起来是这样的(光标无法选择前两行)。但是,根据System.out.println()
,text.getText()的内容是正确的-那么,它看起来像是一个渲染问题
THIS
THISIS ONE
这不可能是第一次有人尝试为每个文件实现独立的撤消缓冲区吗?很明显,我在文档模型上做了一些错误的事情,并且固有地破坏了它,但是我正在寻找一些关于如何最好地解决这个问题的建议
SSCCE的代码如下所示:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;
public class SSCCE extends JFrame implements ActionListener, UndoableEditListener {
private final JLabel labTextArea;
private final JTextArea textArea;
private final JScrollPane scrollTextArea;
private final Document docTextArea;
private final JButton bOne, bTwo, bUndo;
private final UndoManager uOne, uTwo;
private String sOne, sTwo;
private boolean ignoreEdit = false;
public SSCCE(String[] args) {
setTitle("SSCCE - Short, Self Contained, Correct Example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 200);
setLocationRelativeTo(null);
labTextArea = new JLabel("One");
getContentPane().add(labTextArea, BorderLayout.PAGE_START);
uOne = new UndoManager();
uTwo = new UndoManager();
sOne = new String();
sTwo = new String();
textArea = new JTextArea();
docTextArea = textArea.getDocument();
docTextArea.addUndoableEditListener(this);
scrollTextArea = new JScrollPane(textArea);
getContentPane().add(scrollTextArea, BorderLayout.CENTER);
JPanel pButtons = new JPanel();
bOne = new JButton("One");
bOne.addActionListener(this);
bOne.setFocusable(false);
pButtons.add(bOne, BorderLayout.LINE_START);
bTwo = new JButton("Two");
bTwo.addActionListener(this);
bTwo.setFocusable(false);
pButtons.add(bTwo, BorderLayout.LINE_END);
bUndo = new JButton("Undo");
bUndo.addActionListener(this);
bUndo.setFocusable(false);
pButtons.add(bUndo, BorderLayout.LINE_END);
getContentPane().add(pButtons, BorderLayout.PAGE_END);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(bOne)) {
if (!labTextArea.getText().equals("One")) {
sTwo = textArea.getText();
ignoreEdit = true;
textArea.setText(sOne);
ignoreEdit = false;
labTextArea.setText("One");
}
}
else if (e.getSource().equals(bTwo)) {
if (!labTextArea.getText().equals("Two")) {
sOne = textArea.getText();
ignoreEdit = true;
textArea.setText(sTwo);
ignoreEdit = false;
labTextArea.setText("Two");
}
}
else if (e.getSource().equals(bUndo)) {
if (labTextArea.getText().equals("One")) {
try {
if (uOne.canUndo()) {
System.out.println("Performing Undo for One");
uOne.undo();
System.out.println("Buffer One is now:\n" + textArea.getText() + "\n");
}
else {
System.out.println("Nothing to Undo for One");
}
}
catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
else if (labTextArea.getText().equals("Two")) {
try {
if (uTwo.canUndo()) {
System.out.println("Performing Undo for Two");
uTwo.undo();
System.out.println("Buffer Two is now:\n" + textArea.getText() + "\n");
}
else {
System.out.println("Nothing to Undo for Two");
}
}
catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
}
}
@Override
public void undoableEditHappened(UndoableEditEvent e) {
if (!ignoreEdit) {
if (labTextArea.getText().equals("One")) {
System.out.println("Adding Edit for One");
uOne.addEdit(e.getEdit());
}
else if (labTextArea.getText().equals("Two")) {
System.out.println("Adding Edit for Two");
uTwo.addEdit(e.getEdit());
}
}
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new SSCCE(args);
}
});
}
}
之前,我曾尝试创建Document
类的一个新实例(每个实例引用相同的撤消侦听器),并打算使用JTextArea.setDocument()
而不是JTextArea.setText()
。然而,Document
是一个接口,无法实例化,但是在阅读了mKorbel发布的参考资料后,我尝试使用PlainDocument
类来实现这一点,但效果很好
我决定维护一个HashMap
来包含我的文档
类,并在它们之间切换。当我切换文档
时,我看不到撤消/重做问题-我想是因为我不再破坏文档
更新了下面的SSCCE,它现在使用JTextArea.setDocument()
而不是JTextArea.setText()
。这还具有不需要ignoreEdit
布尔值的优点,因为setDocument()
不会触发UndoableEditEvent
,而setText()
会触发。每个文档
然后引用本地类UndoableEditListener
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;
public class SSCCE extends JFrame implements ActionListener, UndoableEditListener {
private final JLabel labTextArea;
private final JTextArea textArea;
private final JScrollPane scrollTextArea;
private final Document docTextArea;
private final JButton bOne, bTwo, bUndo;
private final UndoManager uOne, uTwo;
private Document dOne, dTwo;
public SSCCE(String[] args) {
setTitle("SSCCE - Short, Self Contained, Correct Example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 200);
setLocationRelativeTo(null);
labTextArea = new JLabel("One");
getContentPane().add(labTextArea, BorderLayout.PAGE_START);
uOne = new UndoManager();
uTwo = new UndoManager();
dOne = new PlainDocument();
dTwo = new PlainDocument();
dOne.addUndoableEditListener(this);
dTwo.addUndoableEditListener(this);
textArea = new JTextArea();
docTextArea = textArea.getDocument();
docTextArea.addUndoableEditListener(this);
textArea.setDocument(dOne);
scrollTextArea = new JScrollPane(textArea);
getContentPane().add(scrollTextArea, BorderLayout.CENTER);
JPanel pButtons = new JPanel();
bOne = new JButton("One");
bOne.addActionListener(this);
bOne.setFocusable(false);
pButtons.add(bOne, BorderLayout.LINE_START);
bTwo = new JButton("Two");
bTwo.addActionListener(this);
bTwo.setFocusable(false);
pButtons.add(bTwo, BorderLayout.LINE_END);
bUndo = new JButton("Undo");
bUndo.addActionListener(this);
bUndo.setFocusable(false);
pButtons.add(bUndo, BorderLayout.LINE_END);
getContentPane().add(pButtons, BorderLayout.PAGE_END);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(bOne)) {
if (!labTextArea.getText().equals("One")) {
textArea.setDocument(dOne);
labTextArea.setText("One");
}
}
else if (e.getSource().equals(bTwo)) {
if (!labTextArea.getText().equals("Two")) {
textArea.setDocument(dTwo);
labTextArea.setText("Two");
}
}
else if (e.getSource().equals(bUndo)) {
if (labTextArea.getText().equals("One")) {
try {
if (uOne.canUndo()) {
System.out.println("Performing Undo for One");
uOne.undo();
System.out.println("Buffer One is now:\n" + textArea.getText() + "\n");
}
else {
System.out.println("Nothing to Undo for One");
}
}
catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
else if (labTextArea.getText().equals("Two")) {
try {
if (uTwo.canUndo()) {
System.out.println("Performing Undo for Two");
uTwo.undo();
System.out.println("Buffer Two is now:\n" + textArea.getText() + "\n");
}
else {
System.out.println("Nothing to Undo for Two");
}
}
catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
}
}
@Override
public void undoableEditHappened(UndoableEditEvent e) {
if (labTextArea.getText().equals("One")) {
System.out.println("Adding Edit for One");
uOne.addEdit(e.getEdit());
}
else if (labTextArea.getText().equals("Two")) {
System.out.println("Adding Edit for Two");
uTwo.addEdit(e.getEdit());
}
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new SSCCE(args);
}
});
}
}
这本身并不是一个答案,而是一个解决同一问题的不同方式的证明
这样做的目的是将UndoableEditListener
包装在一个自我管理的代理中,该代理具有自己的UndoManager
和文档
使用Java 7和Java 8对此进行了测试:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
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.JTextComponent;
import javax.swing.text.PlainDocument;
import javax.swing.undo.UndoManager;
public class UndoExample {
public static void main(String[] args) {
new UndoExample();
}
private int index = 0;
private Map<String, Undoer> mapUndoers;
private JTextArea ta;
private Undoer current;
public UndoExample() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
mapUndoers = new HashMap<>(2);
mapUndoers.put("One", new Undoer());
mapUndoers.put("Two", new Undoer());
ta = new JTextArea(4, 20);
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
JButton btnOne = new JButton("One");
JButton btnTwo = new JButton("Two");
ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
install(e.getActionCommand());
}
};
btnOne.addActionListener(al);
btnTwo.addActionListener(al);
JButton undo = new JButton("Undo");
undo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (current != null) {
current.undo();
}
}
});
JPanel panel = new JPanel(new GridBagLayout());
panel.add(btnOne);
panel.add(btnTwo);
panel.add(undo);
install("One");
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(ta));
frame.add(panel, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected void install(String name) {
Undoer undoer = mapUndoers.get(name);
if (undoer != null) {
current = undoer;
undoer.install(ta);
}
}
public class Undoer implements UndoableEditListener {
private UndoManager undoManager;
private Document doc;
public Undoer() {
undoManager = new UndoManager();
doc = createDocument();
doc.addUndoableEditListener(this);
}
public void undo() {
undoManager.undo();
}
public void undoOrRedo() {
undoManager.undoOrRedo();
}
protected Document createDocument() {
return new PlainDocument();
}
public void install(JTextComponent comp) {
comp.setDocument(doc);
}
@Override
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
}
}
}
导入java.awt.BorderLayout;
导入java.awt.EventQueue;
导入java.awt.GridBagConstraints;
导入java.awt.GridBagLayout;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.util.HashMap;
导入java.util.Map;
导入javax.swing.JButton;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JScrollPane;
导入javax.swing.JTextArea;
导入javax.swing.UIManager;
导入javax.swing.UnsupportedLookAndFeelException;
导入javax.swing.event.UndoableEditEvent;
导入javax.swing.event.UndoableEditListener;
导入javax.swing.text.Document;
导入javax.swing.text.JTextComponent;
导入javax.swing.text.PlainDocument;
导入javax.swing.undo.UndoManager;
公共类示例{
公共静态void main(字符串[]args){
新示例();
}
私有整数指数=0;
私人地图绘制者;
私人住宅区;
私有撤消电流;
公共示例(){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
试一试{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}catch(ClassNotFoundException |实例化Exception | IllegalacessException |不支持ookandfeelException ex){
}
mapendoers=新HashMap(2);
放置(“一个”,新的撤消器());
放置(“两个”,新的撤消器());
ta=新的JTextArea(4,20);
ta.setWrapStyleWord(真);
ta.setLineWrap(真);
JButton btnOne=新JButton(“一”);
JButton btnTwo=新JButton(“两个”);
ActionListener al=新的ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
安装(如getActionCommand());
}
};
btnOne.addActionListener(al);
btnTwo.addActionListener(al);
JButton undo=新JButton(“undo”);
undo.addActionListener(新建ActionListener()){
@凌驾
已执行的公共无效操作(操作事件e){
如果(当前!=null){
current.undo();
}
}
});
JPanel panel=newjpanel(newgridbagloayout());
面板。添加(btnOne);
面板。添加(btnTwo);
面板。添加(撤消);
安装(“一个”);
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.undo.*;
public class SSCCE extends JFrame implements ActionListener, UndoableEditListener {
private final JLabel labTextArea;
private final JTextArea textArea;
private final JScrollPane scrollTextArea;
private final Document docTextArea;
private final JButton bOne, bTwo, bUndo;
private final UndoManager uOne, uTwo;
private Document dOne, dTwo;
public SSCCE(String[] args) {
setTitle("SSCCE - Short, Self Contained, Correct Example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 200);
setLocationRelativeTo(null);
labTextArea = new JLabel("One");
getContentPane().add(labTextArea, BorderLayout.PAGE_START);
uOne = new UndoManager();
uTwo = new UndoManager();
dOne = new PlainDocument();
dTwo = new PlainDocument();
dOne.addUndoableEditListener(this);
dTwo.addUndoableEditListener(this);
textArea = new JTextArea();
docTextArea = textArea.getDocument();
docTextArea.addUndoableEditListener(this);
textArea.setDocument(dOne);
scrollTextArea = new JScrollPane(textArea);
getContentPane().add(scrollTextArea, BorderLayout.CENTER);
JPanel pButtons = new JPanel();
bOne = new JButton("One");
bOne.addActionListener(this);
bOne.setFocusable(false);
pButtons.add(bOne, BorderLayout.LINE_START);
bTwo = new JButton("Two");
bTwo.addActionListener(this);
bTwo.setFocusable(false);
pButtons.add(bTwo, BorderLayout.LINE_END);
bUndo = new JButton("Undo");
bUndo.addActionListener(this);
bUndo.setFocusable(false);
pButtons.add(bUndo, BorderLayout.LINE_END);
getContentPane().add(pButtons, BorderLayout.PAGE_END);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(bOne)) {
if (!labTextArea.getText().equals("One")) {
textArea.setDocument(dOne);
labTextArea.setText("One");
}
}
else if (e.getSource().equals(bTwo)) {
if (!labTextArea.getText().equals("Two")) {
textArea.setDocument(dTwo);
labTextArea.setText("Two");
}
}
else if (e.getSource().equals(bUndo)) {
if (labTextArea.getText().equals("One")) {
try {
if (uOne.canUndo()) {
System.out.println("Performing Undo for One");
uOne.undo();
System.out.println("Buffer One is now:\n" + textArea.getText() + "\n");
}
else {
System.out.println("Nothing to Undo for One");
}
}
catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
else if (labTextArea.getText().equals("Two")) {
try {
if (uTwo.canUndo()) {
System.out.println("Performing Undo for Two");
uTwo.undo();
System.out.println("Buffer Two is now:\n" + textArea.getText() + "\n");
}
else {
System.out.println("Nothing to Undo for Two");
}
}
catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
}
}
@Override
public void undoableEditHappened(UndoableEditEvent e) {
if (labTextArea.getText().equals("One")) {
System.out.println("Adding Edit for One");
uOne.addEdit(e.getEdit());
}
else if (labTextArea.getText().equals("Two")) {
System.out.println("Adding Edit for Two");
uTwo.addEdit(e.getEdit());
}
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new SSCCE(args);
}
});
}
}
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
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.JTextComponent;
import javax.swing.text.PlainDocument;
import javax.swing.undo.UndoManager;
public class UndoExample {
public static void main(String[] args) {
new UndoExample();
}
private int index = 0;
private Map<String, Undoer> mapUndoers;
private JTextArea ta;
private Undoer current;
public UndoExample() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
mapUndoers = new HashMap<>(2);
mapUndoers.put("One", new Undoer());
mapUndoers.put("Two", new Undoer());
ta = new JTextArea(4, 20);
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
JButton btnOne = new JButton("One");
JButton btnTwo = new JButton("Two");
ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
install(e.getActionCommand());
}
};
btnOne.addActionListener(al);
btnTwo.addActionListener(al);
JButton undo = new JButton("Undo");
undo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (current != null) {
current.undo();
}
}
});
JPanel panel = new JPanel(new GridBagLayout());
panel.add(btnOne);
panel.add(btnTwo);
panel.add(undo);
install("One");
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(ta));
frame.add(panel, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected void install(String name) {
Undoer undoer = mapUndoers.get(name);
if (undoer != null) {
current = undoer;
undoer.install(ta);
}
}
public class Undoer implements UndoableEditListener {
private UndoManager undoManager;
private Document doc;
public Undoer() {
undoManager = new UndoManager();
doc = createDocument();
doc.addUndoableEditListener(this);
}
public void undo() {
undoManager.undo();
}
public void undoOrRedo() {
undoManager.undoOrRedo();
}
protected Document createDocument() {
return new PlainDocument();
}
public void install(JTextComponent comp) {
comp.setDocument(doc);
}
@Override
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
}
}
}