新项目已经开始,GUI上的改变还是一如既往的大,多,快!做GUI的真苦逼!
因为产品中有某种流程的概念,Boss需要一种能显示流程顺序且兼有Navigator功能的Bar。它在界面底部,用户可以用来做页面跳转。
Eclipse RCP使用SWT作为其图形库。在一顿乱Google之后,没有发现有现成的控件,所以只能自己做一个了。其实不是很想做,但作为一名GUI工程师,也不能总用标准控件吧。其实做控件并不难,以前在用wxWidgets,也做过几个控件,其实要点就几步,主要是费时调试。想想那会为了个控件调到凌晨4、5点,心里就觉得酸,:(。但这几个我费心做出的控件一直在产品中使用,算是一种安慰吧!
在Eclipse Corner Article里,找了篇文章(在Reference里),感觉和wxWidgets差不多。毕竟还是一个简单控件,试了一下,成功了, 同时感慨SWT有很多牛X的功能,远不是wxWidgets可以比的。
归纳几个要点。
控件重载
做自定义控件的第一步就是重载。SWT有两个比较适用的父类,
- org.eclipse.swt.widgets.Canvas
- org.eclipse.swt.widgets.Composite
Composite主要用来把其他控件compose在一起;Canvas则在给你提供一个画布,所有的显示行为都自己定义。这里显然Canvas更适合我。
计算大小
Parent控件或container在使用你的控件时,并不知道你的控件应该有多大。所以我们要提供Preferred Size。
在SWT中,你要重载compusiteSize(int wHint, int hHint, boolean changed). 这部分要根据控件中不同的内容来定,如图的大小,字符串的显示大小,它们怎么组织的。花点时间总是可以写出来的。
提一点,在这个函数中,你可能需要算字符串的长宽,所以你需要GC,所以你可以new一个然后用stringExtend计算大小。
|
GC gc = new GC(this); ... gc.stringExtend(aLabel); |
重绘
有了大小了,下面就要告诉别人怎样把自己画出来。SWT里的办法是增加一个PaintListener.
|
addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { AdvNavBar.this.paintControl(e); } }); |
在paintControl函数里,你就尽情地在GC上画吧。
事件响应
显示出来之后,和用户的交互也很重要。这里就要增加键盘和鼠标的事件。我的控件比较简单,只有鼠标事件。处理过程就是
- 响应鼠标事件
- 做点击测试
- 转换事件
- 找事件监听器处理
首先响应鼠标事件,
|
addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { AdvNavBar.this.mouseDown(e); } }); |
点击测试就是找出鼠标事件发生在哪个区域,里面会有一些Rectangle.contains()等类似的函数。有兴趣请在后面的Github上看看我的代码。
事件转换就是当你识别出事件后,把原来的鼠标事件做一个翻译包装,然后转发。比如在我的控件里,我要识别用户点在哪个Flow的节点上,所以我包装了一个节点变化的事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
import java.util.EventObject; /** * Event for advanced navigation bar change. * @author yli * */ public class AdvNavBarChangeEvent extends EventObject { private static final long serialVersionUID = 1L; private int formerTab; private int currentTab; public AdvNavBarChangeEvent(Object source, int theFormerTab, int theCurrentTab) { super(source); formerTab = theFormerTab; currentTab = theCurrentTab; } public int getFormerTab() { return formerTab; } public int getCurrentTab() { return currentTab; } } |
再做一个监听器给外部代码,某种简单的Observer的模式。
|
import java.util.EventListener; /** * Listener for advanced navigation bar change. * @author yli * */ public interface AdvNavBarChangeListener extends EventListener { void barChanged(AdvNavBarChangeEvent event); } |
在控件里添加add/remove的接口,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
/** * Add change listener. * * @param listener * the listener. */ public void addBarChangeListener(AdvNavBarChangeListener listener) { barChangeListeners.add(listener); } /** * Remove a change listener. * * @param listener * the listener. */ public void removeBarChangeListener(AdvNavBarChangeListener listener) { barChangeListeners.remove(listener); } |
其他要点
这几步就是最重要的。还有一些小的要点,
- SWT中要注意把Color,Font等资源回收(dispose)
- SWT可以用gc.setAntialias(SWT.ON)打开抗锯齿
- 为了避免控件闪烁(flickering),特别是在频繁Resize里,可以在构造控件里加上SWT.DOUBLE_BUFFERED的style。
后两项都是wxWidgets无法比的,需要比较复杂的实现。
Github
代码还在准备中,很快上传。。。
References