Java编写的GUI程序里面,AWT,SWT,QT Jambi都是基于C++ Dll绘制界面,也就是所谓的重量级界面,而Swing是通过Java2D绘制出来的,完全基于java实现,是轻量级界面。Java的这个轻量级界面有对应于每个操作系统平台的Look and Feel,可以把程序装饰得跟本机程序一样。拿Windows里面的Look and Feel来说,几近以假乱真的程度,不过我还是可以通过一点来很容易的区分Java程序,那就是文本框对右键是没有任何反应的,普通的文本框,右键的时候都会有一个菜单,Cut,Copy,Paste,Select All,而Java的文本框?Nothing。这个缺陷其实不算大问题,Ctrl+C,Ctrl+v。。。就可以实现上面的这些功能,不过,想当初我学电脑的时候,也有很长很长一段时间不知道CTRL+C这些快捷键的,所以怎么说没有右键菜单都让人会觉得Swing在UI这块还是不够Professional,呵呵。 还记得去年实习,在公司做Swing的时候,就有这个想法,那个时候也花了大半天的时间去搜索现成的东东,不过没有找到,其实现在想想这个东西还是跟搜索的关键字有关,从技术的角度来讲,用中文搜跟用英文搜差别太大了,我今天又突然想起这个问题,用英文搜索了一下,结果第一页就找到答案了,呵呵,看来boldtech没白来,起码英文用得多一点了,对学习技术有帮助。 我首先搜索到的一篇文章,写文章的人也是因为对这个问题诟病很久了,它在文章里面提到要实现其实也不难,两种理所当然的方式,一是扩展这些文本组件,二是添加全局的MouseListener,实现Popup,不过这两种方式都被他否定了,因为这两种方式的代码量都比较多,前者需要扩展多种文本组件,TextField,TextArea之类,而且需要将之前的TextField,TextArea做一遍替换,后者则是针对每个Frame都要添加一遍Listener。然后作者提到了一种方案,通过自定义EventQueue来实现,主要的代码如下:
// @author Santhosh Kumar T - santhosh@in.fiorano.com public class MyEventQueue extends EventQueue{ protected void dispatchEvent(AWTEvent event){ super.dispatchEvent(event); // interested only in mouseevents if(!(event instanceof MouseEvent)) return; MouseEvent me = (MouseEvent)event; // interested only in popuptriggers if(!me.isPopupTrigger()) return; // me.getComponent(...) retunrs the heavy weight component on which event occured Component comp = SwingUtilities.getDeepestComponentAt(me.getComponent(), me.getX(), me.getY()); // interested only in textcomponents if(!(comp instanceof JTextComponent)) return; // no popup shown by user code if(MenuSelectionManager.defaultManager().getSelectedPath().length>0) return; // create popup menu and show JTextComponent tc = (JTextComponent)comp; JPopupMenu menu = new JPopupMenu(); menu.add(new CutAction(tc)); menu.add(new CopyAction(tc)); menu.add(new PasteAction(tc)); menu.add(new DeleteAction(tc)); menu.addSeparator(); menu.add(new SelectAllAction(tc)); Point pt = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), tc); menu.show(tc, pt.x, pt.y); } } 很简单?的确,只是在MouseEvent里面对Event进行过滤,对右键菜单,并且是来自文本组件的右键菜单添加Popup菜单, 这种方案的确比较简单,最终只需要一行代码, Toolkit.getDefaultToolkit().getSystemEventQueue().push(new MyEventQueue()); 将这个EventQueue加到系统的EventQueue 中就Ok了,不过这个贴发出来以后就有很多反对的声音,主要是这种对全局右键事件的监听可能会打破原有的事件监听体系。 所以这个方案也只是 看上去很美,不过后面有人提到Swinglab的SwingX项目里面通过在look And Feel 里面添加 auxiliary LF 来实现,刚好我在IPSeeker里面 用到了SwingX项目,结果通过一行代码也实现了右键菜单, UIManager.addAuxiliaryLookAndFeel(new ContextMenuAuxLF()); 其中ContextMenuAuxLF位于SwingX中,org.jdesktop.swingx.plaf.ContextMenuAuxLF,不过这个文件的代码我没怎么看懂,大概就是 通过在重新TextUI,在其中加入每个textComponent对应的Action所生成的Popup。代码都不多,看似很简洁,很美, IPSeeker中右键菜单截图:
- 空白文本框中的右键菜单
- 选中文字后右键菜单
- TextArea中的右键菜单
SwingX 实现分析: SwingX是开源的,这点很不错,直接看源代码就可以知道是怎么实现的了,我感觉编程不是一个技术活,经过一段时间的 培训,人人都可以编程,关键要是编出很好看的代码,那才叫牛逼,就像写文章,每个人都会写,但只有少数人能够成为 作家。扯远了,前面也提到SwingX的实现方式主要是通过 UIManager.addAuxiliaryLookAndFeel(new ContextMenuAuxLF());给当前的UIManager添加一个辅助的LookAndFeel,这个 特性很重要,很多时候重新一个LookAndFeel所需要的工作量是相当巨大而且可能是没必要的,这个时候通过调整部分UI, 作为一个辅助的LookAndFeel来添加就显得尤为重要,说白了这个辅助的LookAndFeel会覆盖当前LookAndFeel当中已有的 部分,而辅助的LookAndFeel没有实现的那些部分让又原有的LookAndFeel负责。
- 下面介绍下ContextMenuAuxLF(注释部分用绿色标出,下同):
/* * $Id: ContextMenuAuxLF.java,v 1.5 2006/03/30 10:19:12 kleopatra Exp $ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jdesktop.swingx.plaf; import javax.swing.LookAndFeel; import javax.swing.UIDefaults; /** * Support for context dependent popup menus. * * It's meant to be used as a auxiliary LF on top of main LF: * *** * There are core-issues involved, which might or might not * impair its usefulness, for details please see a thread in * the SwingLabs forum:* UIManager.addAuxiliaryLookAndFeel(new ContextMenuAuxLF()); *
** * * Experimental: default context menus for textcomponents/scrollbars * * * * @author Jeanette Winzenburg */ public class ContextMenuAuxLF extends LookAndFeel { private UIDefaults myDefaults; public String getName() { return "ContextMenuAuxLF"; } public String getID() { return getName(); } public String getDescription() { return "Auxiliary LF to Support Context Dependent Popups"; } public boolean isNativeLookAndFeel() { return false; } public boolean isSupportedLookAndFeel() { return true; } public UIDefaults getDefaults() { if (myDefaults == null) { initDefaults(); } return myDefaults; } private void initDefaults() { myDefaults = new MyUIDefaults(); Object[] mydefaults = { "TextFieldUI", "org.jdesktop.swingx.plaf.ContextMenuAuxTextUI", "EditorPaneUI", "org.jdesktop.swingx.plaf.ContextMenuAuxTextUI", "PasswordFieldUI", "org.jdesktop.swingx.plaf.ContextMenuAuxTextUI", "TextAreaUI", "org.jdesktop.swingx.plaf.ContextMenuAuxTextUI", "TextPaneUI", "org.jdesktop.swingx.plaf.ContextMenuAuxTextUI", "ScrollBarUI", "org.jdesktop.swingx.plaf.ContextMenuAuxScrollBarUI", }; myDefaults.putDefaults(mydefaults); /*关键在于这里,UIDefaults其实是一个HashTable,通过观察你也许就会发现,这里用来初始化UIDefaults的 是一些键值对,键是UI,Name是这个UI对应的实现。这里把TextField,EditorPane,PasswordField,TextAreaUI都初始化 成 "org.jdesktop.swingx.plaf.ContextMenuAuxTextUI"实现 */ } /** * UIDefaults without error msg. * */ private static class MyUIDefaults extends UIDefaults { /** * Overridden to do nothing. * There will be many errors because this is incomplete as * of component types by design * */ @Override protected void getUIError(String msg) { } } }
- ContextMenuAuxLF引用到ContextMenuAuxTextUI
* $Id: ContextMenuAuxTextUI.java,v 1.5 2005/10/24 13:20:45 kleopatra Exp $ * * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package org.jdesktop.swingx.plaf; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseListener; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.TextUI; import javax.swing.text.BadLocationException; import javax.swing.text.EditorKit; import javax.swing.text.JTextComponent; import javax.swing.text.View; import javax.swing.text.Position.Bias; /** * @author Jeanette Winzenburg */ public class ContextMenuAuxTextUI extends TextUI { private MouseListener mouseHandler; public static ComponentUI createUI(JComponent c) { return new ContextMenuAuxTextUI(); //单态模式 } public void installUI(JComponent comp) { comp.addMouseListener(getMouseListener());//在InstallUI的时候给组件加上鼠标侦听 } public void uninstallUI(JComponent comp) { comp.removeMouseListener(getMouseListener());//在卸载UI的时候移除监听 } private MouseListener getMouseListener() { if (mouseHandler == null) { mouseHandler = createPopupHandler();//创建新的PopupHandler,处理右键弹出事件 } return mouseHandler; } private MouseListener createPopupHandler() { return new ContextMenuHandler(createContextSource()); } private ContextMenuSource createContextSource() { return new TextContextMenuSource(); } public void update(Graphics g, JComponent c) { } public Rectangle modelToView(JTextComponent t, int pos) throws BadLocationException { // TODO Auto-generated method stub return null; } public Rectangle modelToView(JTextComponent t, int pos, Bias bias) throws BadLocationException { // TODO Auto-generated method stub return null; } public int viewToModel(JTextComponent t, Point pt) { // TODO Auto-generated method stub return 0; } public int viewToModel(JTextComponent t, Point pt, Bias[] biasReturn) { // TODO Auto-generated method stub return 0; } public int getNextVisualPositionFrom(JTextComponent t, int pos, Bias b, int direction, Bias[] biasRet) throws BadLocationException { // TODO Auto-generated method stub return 0; } public void damageRange(JTextComponent t, int p0, int p1) { // TODO Auto-generated method stub } public void damageRange(JTextComponent t, int p0, int p1, Bias firstBias, Bias secondBias) { // TODO Auto-generated method stub } public EditorKit getEditorKit(JTextComponent t) { // TODO Auto-generated method stub return null; } public View getRootView(JTextComponent t) { // TODO Auto-generated method stub return null; } }
- ContextMenuAuxTextUI中添加的ContextMenuHandler
- ContextMenuHandler用到的TextContextMenuSource
0 评论:
发表评论