由于JTextArea是一个二维的输入组件,因此[Enter]键在JTextArea中代表的意义只是单纯的换行字符而不再是一个事件驱动的 切入点。那么我们该 如何来处理JTextArea的事件呢?还记得我们在前面介绍过Listener的机制吗?相同的,我们一样需要使用 Listener的机制来处理发生在JTextArea中的事件,只是不再是以前提到的ActionListener了。在JTextArea中使用的Listener有两 种,一个是UndoableEditListener,另一个是DocumentListener.UndoableEditListener interface是负责纪录JTextArea中所有操作 发生的顺序并且可以运行还原上一步的功能。这个功能在目前的软件中应用相当广泛,如文书编辑软件Word中的复原功能、小画家 中的复原功能,相信大家都有使用过。DocumentListener interface则是纪录发生在JTextArea中所有的事件(如键入字符、删除 字符、剪下、贴上)并将所有的事件以树状的层次式结构组织起来;也就是说当JTextArea中的内容有任何变动时,会DocumentEvent ,此时必须使用DocumentListener接口中的方法来处理此事件。我们来看下面这个范例,使JTextArea具有复原的功能:
import java.awt.*; import java.awt.event.*; import javax.swing.*; /*由于会使用到复原和事件驱动功能,因此需要将javax.swing.undo和javax.swing.event两个package包含进来 */ import javax.swing.undo.*; import javax.swing.event.*; /*JTextArea4类继承JFrame类并实作UndoableEditListener interface.实作UndoableEditListener interface就必须要编写其中的 *undoableEditHappened(). */ public class JTextArea4 extends JFrame implements UndoableEditListener{ private UndoableEdit edit; private JTextArea jta; private JTextArea message; private JMenuItem undoitem; private JMenuItem redoitem; public JTextArea4(){ super("JTextArea4"); jta = new JTextArea(); jta.getDocument().addUndoableEditListener(this);//将JTextArea加入UndoableEditListener. message = new JTextArea(); message.setEditable(false);//利用setEditable()方法将另一个JTextArea设置为不可编辑. JPanel p1 = new JPanel(); p1.setLayout(new GridLayout(1,1)); p1.setBorder(BorderFactory.createTitledBorder("Edit Area")); p1.add(new JScrollPane(jta)); //--begin:分别将两个JTextArea通过JPanel放到JFrame中。 JPanel p2 = new JPanel(); p2.setLayout(new GridLayout(1,1)); p2.setBorder(BorderFactory.createTitledBorder("Message")); p2.add(new JScrollPane(message)); getContentPane().setLayout(new GridLayout(2,1)); getContentPane().add(p1); getContentPane().add(p2); //--end //建立目录菜单并放置到JFrame中. JMenuBar bar = new JMenuBar(); JMenu theMenu = new JMenu("Edit"); undoitem = new JMenuItem("Undo"); redoitem = new JMenuItem("Redo"); theMenu.add(undoitem); theMenu.add(redoitem); bar.add(theMenu); updateMenuItem();//构造目录菜单 setJMenuBar(bar); setSize(300,300); //采用inner class方式,分别构造菜单选项被点选后的运行操作。分别调用undo(),redo()方法来完成. undoitem.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ev){ edit.undo(); updateMenuItem();//运行undo功能 message.append("- Undo -\n"); } }); redoitem.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ev){ edit.redo(); updateMenuItem();//运行redo功能 message.append("- Redo -\n"); } }); }//end of JTextArea4() public void undoableEditHappened(UndoableEditEvent ev){ StringBuffer buf = new StringBuffer(200); /*当用户在Text Area中有所操作时,就可以用getEdit()方法取得UndoableEdit对象,此对象纪录着刚刚用户的操作,因 *此可由些对象的undo()或redo()达到取消或复原的功能. */ edit = ev.getEdit(); buf.append("undoableEdit:"); buf.append(edit.getPresentationName()); buf.append("\n"); message.append(buf.toString()); updateMenuItem(); }//end of undoableEditHappened() //判断是否此时是否可以运行undo或redo的功能,并且改变目录菜单的状态值. public void updateMenuItem(){ if (edit != null){ undoitem.setEnabled(edit.canUndo()); redoitem.setEnabled(edit.canRedo()); undoitem.setText(edit.getUndoPresentationName()); redoitem.setText(edit.getRedoPresentationName()); }else{ undoitem.setEnabled(false); redoitem.setEnabled(false); undoitem.setText("Undo"); redoitem.setText("Redo"); } }//end of updateMenu() public static void main(String args[]) { JFrame f = new JTextArea4(); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); f.show(); }//end of main() }//end of class JTextArea4
我们在前面提到Enter键在JTextArea中不再是事件驱动的切入点,因此我们要利用Listener的机制来控制JTextArea的事件驱动 。但是,我们要怎么样知道在JTextArea中的数据内容呢?这就要了解JTextArea的存储模式了,JTextArea把输入区内的每一行当成 一个独立的单无(Element),并依照Document内规划的树状结构来存储,也就是说在JTextArea的第一行属于Element 0、第二行属于 Element 1、第三行属于Element 2等等。不论我们在费心的去处理。接下来我们来看看,Element和DocumentListener interface的 用法。我们改写JTextArea4.java加入DocumentListener,将程序存储为JTextArea5.java.
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.undo.*; import javax.swing.event.*; import javax.swing.text.*; public class JTextArea5 extends JFrame implements UndoableEditListener, DocumentListener { private UndoableEdit edit; private JTextArea jta; private JTextArea message; private JMenuItem undoitem; private JMenuItem redoitem; public JTextArea5() { super("JTextArea"); jta = new JTextArea(); jta.getDocument().addUndoableEditListener(this); jta.getDocument().addDocumentListener(this); message = new JTextArea(); message.setEditable(false); JPanel p1 = new JPanel(); p1.setLayout(new GridLayout(1, 1)); p1.setBorder(BorderFactory.createTitledBorder("Edit Area")); p1.add(new JScrollPane(jta)); JPanel p2 = new JPanel(); p2.setLayout(new GridLayout(1, 1)); p2.setBorder(BorderFactory.createTitledBorder("Message")); p2.add(new JScrollPane(message)); getContentPane().setLayout(new GridLayout(2, 1)); getContentPane().add(p1); getContentPane().add(p2); JMenuBar bar = new JMenuBar(); JMenu theMenu = new JMenu("Edit"); undoitem = new JMenuItem("Undo"); redoitem = new JMenuItem("Redo"); theMenu.add(undoitem); theMenu.add(redoitem); bar.add(theMenu); updateMenuItem(); setJMenuBar(bar); setSize(300, 300); undoitem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { edit.undo(); updateMenuItem(); message.append("- Undo -\n"); } }); redoitem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { edit.redo(); updateMenuItem(); message.append("- Redo -\n"); } }); } // end of JTextArea5 public void undoableEditHappened(UndoableEditEvent ev) { StringBuffer buf = new StringBuffer(200); edit = ev.getEdit(); buf.append("undoableEdit:"); buf.append(edit.getPresentationName()); buf.append("\n"); message.append(buf.toString()); updateMenuItem(); }// end of undoableEditHappened() public void updateMenuItem() { if (edit != null) { undoitem.setEnabled(edit.canUndo()); redoitem.setEnabled(edit.canRedo()); undoitem.setText(edit.getUndoPresentationName()); redoitem.setText(edit.getRedoPresentationName()); } else { undoitem.setEnabled(false); redoitem.setEnabled(false); undoitem.setText("Undo"); redoitem.setText("Redo"); } }// end of updateMenu() public void showDE(DocumentEvent de) { StringBuffer debuf = new StringBuffer(100); debuf.append(de.getType()); debuf.append("Offset:"); debuf.append(de.getOffset()); debuf.append("Length:"); debuf.append(de.getLength()); Element Eroot = jta.getDocument().getDefaultRootElement(); DocumentEvent.ElementChange Echange = de.getChange(Eroot); if (Echange == null) { debuf.append("(No Element Change)"); } else { debuf.append("Element Change:index"); debuf.append("Echange.getIndex()"); } debuf.append("\n"); message.append(debuf.toString()); } public void changedUpdate(DocumentEvent de) { showDE(de); } public void insertUpdate(DocumentEvent de) { showDE(de); } public void removeUpdate(DocumentEvent de) { showDE(de); } public static void main(String[] args) { JFrame f = new JTextArea5(); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); f.show(); } }