站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题About My Editor (2)

About My Editor (2)

摘要: About My Editor (2)

这篇文章并不太适合Java高手和刚要开始学Java的人看.如果你刚弄清楚Java编程是怎么回事,并且想用Java提供swing组件做一些简单的程序,以此来巩固对Java编程学习的话,那你算是找对了.我会披露swing组件中的一些鲜为人知的方法.希望这几篇文章能够成为你在学习Java程序设计道路中的一块铺路石,助你顺利攀到Java程序设计颠峰. afritxia.student@sina.com
本篇话题: 文本编辑区设计.
--------------------------------------------------------------------------------
我的JMDEditor的编辑区使用的是非常简单的JTextArea组件.说它简单是因为它已经们实现了很多常用的功能:Cut,Copy,Paste,Select,SelectAll...甚至还可以设置被选取文本的背景色.所以我们并不用费心去写这些功能.
但是,要做一个象样的记事本程序,光有这点功能显然是不够的.看看JDK自带的记事本程序,就连那个还有多次"撤消"与"重做"的功能呢.可它是怎样实现的呢?做了一大堆的AbstractAction类的派生类,其中的UndoAction与RedoAction就是这样来的:
class UndoAction extends AbstractAction { public UndoAction(){ }
public void actionPerformed(ActionEvent e){ // Action Code // 执行撤消操作 }
public void update(){ // Update Code // 如果当前文本无法再进行撤消,则菜单中的"Undo"就不能被选择了 } }
当然,还要有这些东西才能成事:
JTextArea editor=new JTextArea(); UndoableEditListener undoHandler=new UndoHandler(); // ?? UndoManager undo=new UndoManager(); // 撤消管理器? UndoAction undoAction=new UndoAction(); editor.getDocument().addUndoableEditListener(undoHandler);
class UndoHandler implements UndoableEditListener // ? { public void undoableEditHappened(UndoableEditEvent e){ undo.addEdit(e.getEdit()); // ... } }
JMenuItem undoMenuItem=new JMenuItem("Undo"); undoMenuItem.addActionListener(undoAction);
try{ // UndoAction中的撤消操作代码 undo.undo(); }catch(CannotUndoException ex){ // Throws Exception }
我已经乱了!虽然功能比较完善,但是很容易就会让象我一样的初学者晕头转向.所以我千方百计的简化了此操作.
JTextArea editor=new JTextArea(); UndoManager undo=new UndoManager(); // 撤消管理器? undo.setLimit(5); // 5步撤消 editor.getDocument().addUndoableEditListener(new UndoableEditListener(){ public void undoableEditHappened(UndoableEditEvent e) { undo.addEdit(e.getEdit()); } });
JMenuItem undoMenuItem=new JMenuItem("Undo"); undoMenuItem.addActionListener(...);
public void actionPerformed(ActionEvent e){ String cmd=e.getActionCommand(); // ... if(cmd.equals("Undo")) try{ undo.undo(); // Call undo.redo() if you need redo }catch(CannotUndoException ex){ } // ... }
其实看起来也没简化多少.不过我省掉了UndoableEditListener,这样看起来就没有那么多的弯弯绕了.(更简单的方式?目前我是找不到了)
作为一个程序员,在进行编码时经常会遇到编译器给出的错误提示:
Error! ... ... ... ... (17)
最后给出的是错误的所在行.那么我要做的就是将光标移到文件第一行,然后一下下的数出17行来,再然后解决问题.可是如果错误是在第1234行怎么办?还用土办法?那无异于徒步登月!最好来个行列显示功能.起初,我写了一个行列显示的算法.那不值一提,因为随着文本中的字数渐多时,这个算法几乎是以死机的方式运行的.我想JTextArea中应该有这样的方法,可是我寻觅了大半天也是一无所获.最后,所有的嫌疑都被归到
getLineOfOffset(int) 和 getLineStartOffset(int)
两个函数身上.这两个是什么意思?...看来,只有象搭积木一样把他们搭来看看了:
// import javax.swing.event.*;
JTextArea editor=new JTextArea(); editor.addCaretListener(new CaretListener(){ public void caretUpdate(CaretEvent e){ int dot=e.getDot(); int ln, col; ln=col=0; try{ ln=editor.getLineOfOffset(dot); col=dot-editor.getLineStartOffset(ln); System.out.println("["+(ln+1)+","+(col+1)+"]"); }catch (BadLocationException Ex){ } } });
至于getLineOfOffset(int)与getLineStartOffset(int),是个什么地噶活:
// 摘录自: SUN Microsystem jdk1.3.1 / src.jar / JTextArea.java
public int getLineOfOffset(int offset) throws BadLocationException { Document doc = getDocument(); if (offset < 0) { throw new BadLocationException("Can't translate offset to line", -1); } else if (offset > doc.getLength()) { throw new BadLocationException("Can't translate offset to line", doc.getLength()+1); } else { Element map = getDocument().getDefaultRootElement(); return map.getElementIndex(offset); } }
public int getLineStartOffset(int line) throws BadLocationException { Element map = getDocument().getDefaultRootElement(); if (line < 0) { throw new BadLocationException("Negative line", -1); } else if (line >= map.getElementCount()) { throw new BadLocationException("No such line", getDocument().getLength()+1); } else { Element lineElem = map.getElement(line); return lineElem.getStartOffset(); } }
恩!大大地好!可以把他们塞到JEditorPane,JTextPane里去,继续效忠我们Java爱好者.没看懂?各位只管拿去改改随便用就成了.
本篇最后登场的是一个重量级话题:查找与替换
首先,要做一个查找与替换对话框.它继承自JDialog类,并且是可以和主窗体并行的.
public class FindDlg extends JDialog { public FindDlg(JFrame f){ super(f, "Find and Replace.", false); // 用false就能并行 // ... ... } // Find and Replace Code ... }
然后就是最重要的查找与替换功能的实现了:
// KEY: Find function ////////////////////////////////////////////////////////// // Algorithm is ideological: From 'pos' location, cut out 'findStrLen' character // and 'findStr' to compare. If be identical to return, it is different and con- // -tinued to cut out 'findStrLen' character from next location compare with // 'findStr'. editTxtAra: The text area that has been sought. findStr: Find Str- // -ing. direction: Find direction.
public boolean find(JTextArea editTxtAra, String findStr, int direction, boolean checkCase){ if(findStr.equals("")) return(false); pos=editTxtAra.getSelectionEnd(); int findStrLen=findStr.length(); int editTxtAraLen=editTxtAra.getText().length(); String temp=""; if(direction==-1) pos=editTxtAra.getSelectionStart()-1; while(pos>=0&&pos // Why return a boolean value ?
// KEY: Replace function /////////////////////////////////////////////////////// // Algorithm is ideological: If exist selected text, replace it. // editTxtAra: The text area that has been sought. // replaceStr: Use 'replaceStr' replace selection.
private void replace(JTextArea editTxtAra, String replaceStr){ if(editTxtAra.getSelectionStart()!=editTxtAra.getSelectionEnd()) editTxtAra.replaceSelection(replaceStr); }
// KEY: Replace function /////////////////////////////////////////////////////// // Algorithm is ideological: Circulate to seek replacement. // editTxtAra: The text area that has been sought. // findStr: Find String. // replaceStr: Use 'replaceStr' replace selection.
// Why function find return a boolean value ? Are you see ?
private void replaceAll(JTextArea editTxtAra, String findStr, String replaceStr, boolean checkCase){ if(findStr.equals("")) return; int i; editTxtAra.select(0, 0); for(i=0; find(editTxtAra, findStr, +1, checkCase); ++i) // Are you see ?! replace(editTxtAra, replaceStr); JOptionPane.showMessageDialog(FindDlg.this, "Replaced "+i+" occurence(s) in this file.", "INFORMATION", JOptionPane.INFORMATION_MESSAGE); }
// 别怪我的E文不正确,只怪现在的翻译软件都是直来直去的(注释没看懂?别急!翻译软件能看懂.
// 按理说翻译软件是可以再直译回原文的...不成?!...那可就好玩儿了).
我并不想解释我的算法中的每一句话到底是什么意思,因为那是属于算法与数据结构的范畴,非计算机专业的Java爱好者恐怕不会在意什么算法,而且这也离我的文章的主题远了点.不过我还是很希望能有人跟我讨论一下这个算法.(如果你是Java高手,你应该发现这里的替换方法与之前的撤消联起来有点毛病,我还不知道怎么解决)
每当查找完事以后,应该让JTextArea的对象选中一段文本表明已经找到.但是结果是不行!可以找到文本,但无法选中.我用一般的requestFocus()就是这个结果.后来我用的是比request生硬的多的grab, grabFocus(),这才解决了文本无法选中的问题.
一切查找工作都完事了(?),总觉得少了点什么东西.是什么呢?没有默认键(就是对话框刚一出现就被(也永远被)选中的那个按钮).我试了JButton类的setDefaultButton(boolean)的方法,可是这不是我期望的那种效果.不过我还是找到了解决方法:
JDialog dlg=new JDialog(...); JButton OK=new JButton("OK"); dlg.getContentPane().setLayout(new FlowLayout()); dlg.getContentPane().add(OK); dlg.getRootPane().setDefaultButton(OK); // 默认键 dlg.setSize(480, 320); dlg.show();
搞定关键字高亮显示?要是搞定了我肯定会告诉各位的.那是个很难的课题.
要继续写下去吗? (下次是文件I/O)
↑返回目录
前一篇: About My Editor
后一篇: 2001年中国 APEC CEO 峰会上的比尔·盖茨和微软公司