我们可以在组件上绘制一切来定制我们所需要的各种控制,但这篇文章的目的在于使用基本的SWT组件在一个普通的Shell中实现比典型SWT Shell更加美观的效果。
(注意,仅在Windows上进行过测试,在其它平台上可能有所不同) 。
Shell
我们想要一个shell停留在我们的程序的上方(但不是所有程序的上方),但不获取焦点、显示在你的任务管理器或是影响用户关注的对象。 我们也不使用"trim"来给Shell加边框而是绘制自定义的边框。我们所有的要求都可以通过SWT的flags参数方便的创建。我们可以如下创建Shell:
-
_shell = new Shell(Display.getDefault().getActiveShell(), SWT.NO_FOCUS | SWT.NO_TRIM);
由于我们的SWT组件在shell内部并且我们打算对shell本身使用渐变背景,所有的组件都必须是透明的。通过在shell上设置背景色模式为SWT.INHERIT_DEFAULT来实现。它告诉组建继承它们父组件的背景色。在创建shell之后我们简单调用:
-
_shell.setBackgroundMode(SWT.INHERIT_DEFAULT);
那么如何在shell上得到渐变的背景色呢?默认方式下是不支持的。你可以设置一个前景色和一个背景色但也就只能如此了。技巧是我们使用我们想要的颜色绘制一个图像来作为shell的背景。为了在合适的时间做到这一点,我们监听shell上的SWT.Resize事件,代码如下;
-
_shell.addListener(SWT.Resize, new Listener() {
-
@Override
-
public void handleEvent(Event e) {
-
try {
-
-
Rectangle rect = _shell.getClientArea();
-
-
Image newImage = new Image(Display.getDefault(), Math.max(1, rect.width), rect.height);
-
-
GC gc = new GC(newImage);
-
-
-
gc.setForeground(_bgFgGradient);
-
gc.setBackground(_bgBgGradient);
-
gc.fillGradientRectangle(rect.x, rect.y, rect.width, rect.height, true);
-
-
-
gc.setLineWidth(2);
-
gc.setForeground(_borderColor);
-
gc.drawRectangle(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
-
-
gc.dispose();
-
-
-
_shell.setBackgroundImage(newImage);
-
-
-
if (_oldImage != null) {
-
_oldImage.dispose();
-
}
-
_oldImage = newImage;
-
}
-
catch (Exception err) {
-
err.printStackTrace();
-
}
-
}
-
});
内容
我们的shell由3个部分组成,图片在左上角(一个普通的CLabel ) 。 然后一个包含标题文字的标签(也是CLabel ) ,最后一个标签显示我们的信息(一个普通的Label) 。 我们最后使用一个Label而不是CLabel来支持比CLabel更佳的断行和多行效果。 您当然也可以使用几乎任何你想要的组建。 以下是用线框分割的每个标签:
我们使用边缘间隔5px的GridLayout(除顶部)除此之外没有特别的效果创建,所以我就不在这里写出代码了(在底部供下载) 。
一些眼球效果-凋谢
在3.4的SWT中Shells支持alpha值设置,所以我们将以此来实现更美观的"呈现"和"消失",所有我们所需要做的就是在一个线程中循环增加/减少shell的alpha通道值来实现shell的淡入和淡出消失。我们必须在Display线程上来做这些操作,为此提供了一个runnable让Display对象以给定的时间间隔运行。
在多线程的环境下记得始终检查shell是否已经被销毁了。当淡入显示完成后,我们希望shell显示一定时间然后消失掉,所以基本的流程链是:
- 创建shell
- 淡入shell
- 可的X秒
- 淡出shell
- 销毁shell
所有的这些定时器或多或少就如编码中的那样,所以我仅提供了一个例子其余的可以参考下载中的完整代码方面,我们需要解释一些全局的变量。
FADE_IN_STEP
是每次迭代中alpha值的增量。
FINAL_ALPHA
是当shell保持可见时的alpha值。一定透明度的shell将提供更佳的效果,所以默认该值是225(255则为一个完全[不透明]的shell) 。
FADE_TIMER
是alpha变化的时间间隔。当shell达到它的最大alpha值时调用startTimer()方法,这是一个线程,休眠若干秒,然后调用fadeOut()方法。
-
private static void fadeIn(final Shell _shell) {
-
Runnable run = new Runnable() {
-
-
@Override
-
public void run() {
-
try {
-
if (_shell == null || _shell.isDisposed()) { return; }
-
-
int cur = _shell.getAlpha();
-
cur += FADE_IN_STEP;
-
-
if (cur > FINAL_ALPHA) {
-
_shell.setAlpha(FINAL_ALPHA);
-
startTimer(_shell);
-
return;
-
}
-
-
_shell.setAlpha(cur);
-
Display.getDefault().timerExec(FADE_TIMER, this);
-
}
-
catch (Exception err) {
-
err.printStackTrace();
-
}
-
}
-
-
};
-
Display.getDefault().timerExec(FADE_TIMER, run);
-
}
堆叠的通知
最后我们要支持的,是堆叠通知。 假设我们的通知shell的整个生命周期是5秒,如果有额外的通知在第一个通知关闭之前到达呢?我们可以销毁第一个通知,并以后者取代,但更美观的方法是简单地将前者升高,让后者显示在前者的下方。效果如下(截屏时之前的shell开始淡出,所以它们有更高的透明度) :
实现这一效果是相当简单的。 每次通知shell被打开,我们在静态的shell数组中保留一个引用。当它淡出(并销毁)后,从该列表中移除。因此,当一个新的shell到来后我们查看当前数组中已经存在的元素并对每一个旧的shell-heightOfNewNotificationShell
。 因此,旧的shell上移以腾出空间,但继续如前地完成它们的淡入/淡出周期。
进一步改善
当然一切都可以做得更加美观,也许你想要一个“ X ”关闭按钮,或不同颜色的文字,或圆角边框。 当前代码中并没有实现这些,但为你实现大多数功能避免问题。
另外请注意,我目前使用Display.getDefault().getActiveShell()
作为弹出窗口的夫shell。您可能想把它重构为一个永久性的shell以便在任何父shell被销毁或任何弹出窗被显示的时候都能使通知被销毁。
代码不是十全十美的,却可以作为构建的基础(如果你愿意的话) 。
下载代码
我已创建了一个基于Eclipse项目的代码,ImageCache / FontCache / ColorCache(它们的目的是为了缓存图片等系统资源)。 还有一个用来存放图片的jar。 如果你喜欢它们,可以找到更多: Gnome的颜色图标 。
请注意您可能需要调整您的CLASSPATH设置的项目,以对应自己的SWT jar所在。
运行Tester.java然后按下按钮启动通知。在前一个通知消失前再次按下
。随机图像将显示。
下载代码
结论
希望这可让您深入了解如何创建“自定义部件” (没有多少定制绘画)。 玩得开心!