ITEEDU

9-4-2:JTextArea的事件处理:

由于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();
	}
}