从另一个 JtextArea 实时更新一个 JtextArea

Live updating one JtextArea from another JtextArea

我正在尝试编写一个程序来显示两个 Jtextareas,这两个都是可编辑的。目标是当您编辑 textAreaRom(输入罗马数字)时,第二个区域 (textAreaArab) 将显示等效的阿拉伯数字。但问题是我无法让这件事实时发生。我已经阅读过有关使用 DocumentListeners 的信息,但是,这是我第一次进行 GUI 编程,我不确定我将如何实现它。什么都有帮助。我是 GUI 和 Whosebug 的新手,所以请多多关照!

注意:我的转换方法都很完美。

public class ArabicToRomanGUI_Hard extends JFrame
{
private static final long serialVersionUID = 1L;
private static String input = "";
private static String output = "";

//constructor to add text fields to frame
public ArabicToRomanGUI_Hard() 
{   
    //JFrame frame = new JFrame("Convert Back And Forth");
    final JTextField enterRomNumber = new JTextField(20);
    final JTextArea textAreaRom = new JTextArea(20,20);
    final JTextField enterArabNumber = new JTextField(20);
    final JTextArea textAreaArab = new JTextArea(20,20);
    setLayout(new FlowLayout());
    enterRomNumber.setText("Please enter a Roman numeral");
    enterArabNumber.setText("Please enter a Arabic Number");

    textAreaRom.setEditable(true);
    textAreaArab.setEditable(true);

    //textAreaRom.setText(enterRomNumber.getText());
    //textAreaArab.setText(enterArabNumber.getText());

    if(textAreaRom.isFocusOwner() == true)
    {

        textAreaRom.addKeyListener(new KeyAdapter()
            {
                public void keyReleased(KeyEvent e) 
                {      
                    {
                        e.getKeyChar();
                        input += e.getKeyChar();
                        ConversionLogic_Hard.ConvertFromRomanToArabic(input); //convert
                        ConversionLogic_Hard.getCheckFail(); //check if conversion is valid.

                        output = ConversionLogic_Hard.getConvertedRomanNumeral(); //get the conversion

                        while(ConversionLogic_Hard.getCheckFail() == true && textAreaArab.isFocusOwner() == false)
                        {
                            textAreaArab.setText(output);
                        }
                        textAreaArab.setText(input);
                    }
                }
            });             
        }



    getContentPane().add(enterRomNumber, BorderLayout.EAST);
    getContentPane().add(textAreaRom, BorderLayout.WEST);
}

你其实有3个问题...

  1. 在更新第一个文本组件时更新第二个文本组件,这相对容易。
  2. 在更新第二个文本组件时更新第一个文本组件...好吧,这变得非常复杂非常快。
  3. 过滤输入的文本。 Implementing a Document Filter
  4. 这实际上相对容易

现在,真正的问题是#2,这是因为当第一个字段试图更新第二个字段时,您可能很快就会陷入一个令人讨厌的地方,这会触发和事件导致第二个字段更新触发第一个字段和导致第一个字段更新第二个字段的事件......你开始明白了。

幸运的是,Swing 实际上不会让它变得那么糟糕,并且会抛出 IllegalStateException

为了克服这个问题,你需要有某种方法来知道什么时候对 Document 的更新是由从属字段触发的,或者它是由其他东西触发的(比如 setText 被调用,用户在字段中键入或粘贴文本)并忽略其中一些事件

现在,可能有一种非常酷且简单的方法可以做到这一点,但我的大脑今天不在 "simple" 模式,抱歉。

所以,我从自定义 Document 开始,它基本上有一个允许我检查其状态的标志

public class MirrorDocument extends PlainDocument {

    private boolean ignoreUpdates;

    public void setIgnoreUpdates(boolean ignoreUpdates) {
        this.ignoreUpdates = ignoreUpdates;
    }

    public boolean isIgnoreUpdates() {
        return ignoreUpdates;
    }

}

现在,我定义了一个 DocumentListener 来监视对 Document 的更改。这个 DocumentListener 需要一个 "slave" Document ,这个侦听器使用它来更新

public static class DocumentHandler implements DocumentListener {

    private MirrorDocument slaveDocument;
    private boolean ignoreUpdates = false;

    public DocumentHandler(MirrorDocument slaveDocument) {
        this.slaveDocument = slaveDocument;
    }

    @Override
    public void insertUpdate(DocumentEvent e) {
        Document doc = e.getDocument();
        if (doc instanceof MirrorDocument) {
            MirrorDocument md = (MirrorDocument) doc;
            if (!md.isIgnoreUpdates()) {
                try {
                    String text = e.getDocument().getText(e.getOffset(), e.getLength());
                    slaveDocument.setIgnoreUpdates(true);
                    slaveDocument.insertString(e.getOffset(), text, null);
                } catch (BadLocationException ex) {
                    ex.printStackTrace();
                } finally {
                    slaveDocument.setIgnoreUpdates(false);
                }
            }
        }
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
        Document doc = e.getDocument();
        if (doc instanceof MirrorDocument) {
            MirrorDocument md = (MirrorDocument) doc;
            if (!md.isIgnoreUpdates()) {
                try {
                    slaveDocument.setIgnoreUpdates(true);
                    slaveDocument.remove(e.getOffset(), e.getLength());
                } catch (BadLocationException ex) {
                    ex.printStackTrace();
                } finally {
                    slaveDocument.setIgnoreUpdates(false);
                }
            }
        }
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
    }

}

现在,真正复杂的部分是 ignoreUpdates 标志。基本上,这是在事件发生时所做的,首先我们检查触发事件的 DocumentignoreUpdates 标志,如果它是 false,我们继续设置 ignoreUpdates 标记 slaveDocumenttrue,这会阻止它 DocumentListener 处理任何新事件然后更新 slaveDocument

好吧,这可能看起来有点奇怪,我很抱歉,但相信我,这是有道理的……(总有一天……当它发生时,你可以向我解释)

因此,接下来,我们需要创建所有内容并将其粘合在一起...

    JTextArea left = new JTextArea(10, 20);
    JTextArea right = new JTextArea(10, 20);

    MirrorDocument leftDoc = new MirrorDocument();
    MirrorDocument rightDoc = new MirrorDocument();

    left.setDocument(leftDoc);
    right.setDocument(rightDoc);

    leftDoc.addDocumentListener(new DocumentHandler(rightDoc));
    rightDoc.addDocumentListener(new DocumentHandler(leftDoc));

所以我们创建了两个JTextArealeftright。我们创建两个 MirrorDocuments,每个 JTextArea 一个,然后我们创建两个 DocumentHandlers,每个 JTextArea 一个,并提供相反的 Document 作为 slave (leftrightDocrightleftDoc),这允许事件交叉并发生更新

这允许我们设置类似...

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.logging.Level;
import java.util.logging.Logger;
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.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

public class MirrorTextAreas {

    public static void main(String[] args) {
        new MirrorTextAreas();
    }

    public MirrorTextAreas() {
        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 class TestPane extends JPanel {

        public TestPane() {
            JTextArea left = new JTextArea(10, 20);
            JTextArea right = new JTextArea(10, 20);
            setLayout(new GridLayout(1, 2));

            add(new JScrollPane(left));
            add(new JScrollPane(right));

            MirrorDocument leftDoc = new MirrorDocument();
            MirrorDocument rightDoc = new MirrorDocument();

            left.setDocument(leftDoc);
            right.setDocument(rightDoc);

            leftDoc.addDocumentListener(new DocumentHandler(rightDoc));
            rightDoc.addDocumentListener(new DocumentHandler(leftDoc));
        }

    }

    public class MirrorDocument extends PlainDocument {

        private boolean ignoreUpdates;

        public void setIgnoreUpdates(boolean ignoreUpdates) {
            this.ignoreUpdates = ignoreUpdates;
        }

        public boolean isIgnoreUpdates() {
            return ignoreUpdates;
        }

    }

    public static class DocumentHandler implements DocumentListener {

        private MirrorDocument slaveDocument;
        private boolean ignoreUpdates = false;

        public DocumentHandler(MirrorDocument slaveDocument) {
            this.slaveDocument = slaveDocument;
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            Document doc = e.getDocument();
            if (doc instanceof MirrorDocument) {
                MirrorDocument md = (MirrorDocument) doc;
                if (!md.isIgnoreUpdates()) {
                    try {
                        String text = e.getDocument().getText(e.getOffset(), e.getLength());
                        slaveDocument.setIgnoreUpdates(true);
                        slaveDocument.insertString(e.getOffset(), text, null);
                    } catch (BadLocationException ex) {
                        ex.printStackTrace();
                    } finally {
                        slaveDocument.setIgnoreUpdates(false);
                    }
                }
            }
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            Document doc = e.getDocument();
            if (doc instanceof MirrorDocument) {
                MirrorDocument md = (MirrorDocument) doc;
                if (!md.isIgnoreUpdates()) {
                    try {
                        slaveDocument.setIgnoreUpdates(true);
                        slaveDocument.remove(e.getOffset(), e.getLength());
                    } catch (BadLocationException ex) {
                        ex.printStackTrace();
                    } finally {
                        slaveDocument.setIgnoreUpdates(false);
                    }
                }
            }
        }

        @Override
        public void changedUpdate(DocumentEvent e) {
        }

    }

}

好的,这大约是我们需要的一半,接下来我们需要能够过滤输入到其中一个字段中的内容,以便我们可以更改,这可以通过 Implementing a Document Filter