Живое обновление одного JtextArea из другого JtextArea

Я пытаюсь написать программу, которая будет отображать две Jtextareas, обе из которых доступны для редактирования. Цель состоит в том, чтобы при редактировании textAreaRom (вводе римской цифры) вторая область (textAreaArab) отображала эквивалент арабского числа. Но дело в том, что я не могу заставить это происходить в режиме реального времени. Я читал об использовании DocumentListeners, но это один из первых случаев, когда я когда-либо занимался программированием графического интерфейса, и я не уверен, как бы я это реализовал. Все что угодно помогает. Я новичок в графическом интерфейсе, а также в StackOverflow, так что будьте добры!

Примечание. Все мои методы преобразования работают отлично.

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);
}

person user3712626    schedule 08.10.2015    source источник


Ответы (1)


У тебя на самом деле три проблемы...

  1. Обновление второго текстового компонента при обновлении первого, что относительно просто.
  2. Обновляйте первый текстовый компонент, когда обновляется второй... ну, это ОЧЕНЬ сложно, ОЧЕНЬ быстро.
  3. Отфильтруйте текст по мере его ввода. На самом деле это относительно легко сделать с помощью реализации фильтра документов.

Теперь реальная проблема № 2, потому что вы можете очень быстро оказаться в неприятном месте, поскольку первое поле пытается обновить второе поле, которое срабатывает, и событие, которое заставляет второе поле обновлять первое поле, которое срабатывает и событие, которое заставляет первое поле обновлять второе поле... и вы начинаете понимать эту идею.

К счастью, Swing на самом деле не допустит, чтобы все стало так плохо, и выдаст IllegalStateException

Чтобы преодолеть это, вам нужен какой-то способ узнать, когда обновление Document инициируется подчиненным полем или когда оно инициируется чем-то еще (например, setText вызывается, когда пользователь печатает или вставляет текст в поле поле) и игнорировать некоторые из этих событий

Теперь, наверное, есть действительно крутой и простой способ сделать это, но мой мозг сегодня не в «простом» режиме, извините.

Итак, я начинаю с пользовательского 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 берет "ведомый" 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. По сути, когда происходит событие, сначала мы проверяем флаг ignoreUpdates элемента Document, вызвавшего событие, и, если это false, мы устанавливаем флаг ignoreUpdates элемента slaveDocument в значение true, что предотвращает обработку 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));

Итак, мы создаем два JTextAreas, left и right. Мы создаем два MirrorDocument, по одному для каждого JTextArea, затем мы создаем два DocumentHandler, по одному для каждого JTextArea, и предоставляем противоположный Document в качестве ведомого (rightDoc для left и leftDoc для right), это позволяет происходить пересечению событий и обновлению.

Это позволяет нам настроить что-то вроде...

Зеркальные текстовые области

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) {
        }

    }

}

Итак, это примерно половина того, что нам нужно, затем нам нужно иметь возможность фильтровать содержимое, введенное в одно из полей, чтобы мы могли его изменить, это можно сделать с помощью Реализация фильтра документов

person MadProgrammer    schedule 08.10.2015