Java GUI中的文档模型

时间:2011-09-16 02:10:33

标签: java swing document documentlistener

我的GUI中有两个JTextAreas,我在每个JTextArea上都有一个DocumentListener,我正在尝试做的是例如当我在文本区域1中键入abc时,它将采用该文档文本以某种方式修改它并将其输出到Document for JTextArea 2。

使用我的Listener我可以获取源文档我可以获取文本我可以修改文本但是当我尝试将其放回文档时出现错误

线程“AWT-EventQueue-0”中的异常java.lang.IllegalStateException:尝试在通知中进行变异

请帮忙。

由于

以下是一些代码:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Maxi
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
import javax.swing.*;
import javax.swing.text.*;

public class Test {

    static JFrame frame = new JFrame("CaesarEncipherGUI");
     static JPanel panel = new JPanel();
     static JTextArea area = new JTextArea(5,20);



     static JTextArea area1 = new JTextArea(5,20);


    static class MyDocumentListener2 implements DocumentListener {

    public void insertUpdate(DocumentEvent e) {
        updateLog(e,"");
    }
    public void removeUpdate(DocumentEvent e) {
        updateLog(e,"");

    }


    public void changedUpdate(DocumentEvent e) {

    }    


public void updateLog(DocumentEvent e, String action){


Document doc = (Document)e.getDocument();



try{


  System.out.println("Action detected  "+doc.getProperty("type"));

String text = doc.getText(0, doc.getLength());

doc.insertString(0, "hey", null); //heres the line that throws the error.



//mutation of text here

}catch (BadLocationException catchme2){}



}
}

        public static void main(String[] args){



            area.getDocument().addDocumentListener(new MyDocumentListener2());

         //initialize
         frame.setResizable(false);
         frame.setBounds(300, 300, 235, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);



panel.add(area);
panel.add(area1);
frame.add(panel);
frame.setSize(235,400);
frame.setVisible(true);


     }



}

1 个答案:

答案 0 :(得分:4)

可能您正在尝试让DocumentListener更改正在侦听的同一Document上的文本。根据{{​​3}}声明不允许这样做:

  

DocumentEvent通知基于JavaBeans事件模型。无法保证向听众传递的顺序,并且必须在对文档进行进一步突变之前通知所有听众。这意味着DocumentListener的实现可能不会改变事件的来源(即关联的文档)。

解决此问题的一种方法是将方法置于Runnable中更改Document的文本,并使用SwingUtilities.invokeLater(...)将其排在EDT上。

另一种解决方案,或许更好,是使用DocumentFilter

DocumentListener的示例:

   static class MyDocumentListener2 implements DocumentListener {
      private boolean updating = false;

      public void insertUpdate(DocumentEvent e) {
         updateLog(e, "");
      }

      public void removeUpdate(DocumentEvent e) {
         updateLog(e, "");

      }

      public void changedUpdate(DocumentEvent e) {

      }

      public void updateLog(DocumentEvent e, String action) {
         if (updating) {
            return;
         }
         updating = true;

         final Document doc = (Document) e.getDocument();

         try {

            System.out.println("Action detected  " + doc.getProperty("type"));

            final String text = doc.getText(0, doc.getLength());

            SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                  try {
                     doc.insertString(0, "hey", null);
                     updating = false;
                  } catch (BadLocationException e) {
                     e.printStackTrace();
                  }
               }
            });

         } catch (BadLocationException catchme2) {
            catchme2.printStackTrace();
         }

      }
   }

DocumentListener和DocumentFilter示例将所有文本转换为大写:

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class Foo003 {
   private static final String ENTER = "enter";

   public static void main(String[] args) {
      final JTextArea myArea = new JTextArea(10, 20);
      final PlainDocument myDocument = (PlainDocument) myArea.getDocument();

      DocumentListener myDocumentListener = new DocumentListener() {
         private boolean changing = false;

         public void removeUpdate(DocumentEvent e) {}

         public void changedUpdate(DocumentEvent e) {
            toUpperCase(myArea, myDocument);
         }

         @Override
         public void insertUpdate(DocumentEvent e) {
            toUpperCase(myArea, myDocument);
         }

         private void toUpperCase(final JTextArea myArea,
               final PlainDocument myDocument) {
            if (changing) {
               return;
            }
            try {
               changing = true;
               final String text = myDocument
                     .getText(0, myDocument.getLength()).toUpperCase();
               SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                     myArea.setText(text);
                     changing = false;
                  }
               });
            } catch (BadLocationException e1) {
               e1.printStackTrace();
            }
         }

      };

      myDocument.addDocumentListener(myDocumentListener);

      JOptionPane.showMessageDialog(null, new JScrollPane(myArea),
            "With DocumentListener", JOptionPane.INFORMATION_MESSAGE);

      myDocument.removeDocumentListener(myDocumentListener);

      myArea.setText("");

      myDocument.setDocumentFilter(new DocumentFilter() {
         @Override
         public void insertString(FilterBypass fb, int offset, String text,
               AttributeSet attr) throws BadLocationException {
            text = text.toUpperCase();
            super.insertString(fb, offset, text, attr);
         }

         @Override
         public void replace(FilterBypass fb, int offset, int length,
               String text, AttributeSet attrs) throws BadLocationException {
            text = text.toUpperCase();
            super.replace(fb, offset, length, text, attrs);
         }
      });
      JOptionPane.showMessageDialog(null, new JScrollPane(myArea),
            "With DocumentFilter", JOptionPane.INFORMATION_MESSAGE);
   }
}

DocumentListeners和DocumentFilters之间的一个主要区别(如果我错了,有人纠正我的话)是DocumentListeners在文档更新后触发,而DocumentFilters在更新之前就会触发。