Advanced SWT Widegts 3rd

前言:哎,核新还没通知我,看来是泡汤了,也是,前天的表现也实在是……还是基础不扎实造成的。感觉现在好多人都是盲目跟风,我也不例外,有什么新出的东西就马上放下手上的进行新的事业,可是大家都忘记了,楼建了越高,它的地基就要打了越深,参天大树的根系和它的枝叶是一样繁茂的!还是去跑29号的招聘会吧~

 Graphics
SWT提供了丰富的图形处理能力,而这功能主要通过了GC这个类实现,首先我们就来看看这个GC类。
Graphics Contexts
GC是你在用SWT进行绘制和显示图形用了最多的类。它提供了大量的基本图形处理函数,在你进行自定义的图形或者自定义小控件的时候显得特别有用和必要。
通过它你可以进行如下操作:   

  • 绘制或者填充一个矩形、椭圆、实线、多边形或者是文本
  • 设置背景和前景颜色
  • 设置裁剪区域
  • 把选定区域复制到图像或者同一控件的其他区域(原文:copy regions to images or other areas of the same widget)
  • 显示图像
  • 设置或得到文本或者字体的属性

Graphics中的一个基本任务就是绘制并填充一个矩形。通过drawRectangle和fillRectangle方法可以做到。drawRectangle使用当前的前景颜色(黑色)来绘制一个矩形。参数(x,y)是用来设定矩形的宽和高的。在例子中,左上角坐标是(3,5),左下角的坐标就是(3+20, 5+25)=(23,30)。

gc.drawRectangle( 3 5 20 25 );

和DrawRectangle很相似,fillRectangle是用当前的背景(当前的默认值是灰色)色来填充的。直到我们设置了背景颜色,填充的矩形才会显现在屏幕上。

gc.fillRectangle( 30 5 20 25 );

其实还有一个获得颜色的简单的方法就是通过Display对象的getSystemColor方法。当然也可以自定义颜色,这马上会在后面提到。

Color blue  =  display.getSystemColor(SWT.COLOR_BLUE);
Color red 
=  display.getSystemColor(SWT.COLOR_RED);

现在我们设定了一些颜色,我们就来设定一下前景色吧。绘制实线后还有其他一些基础的绘制函数。注意椭圆和多边形相应的函数是不相同的。椭圆的填充函数的参数有点类似矩形。而多边形是通过一组交替的x和y坐标的整形一维数组做参数传入的。

gc.setForeground(blue);
gc.drawLine(
80 20 100 80 );
gc.drawOval(
40 40 10 10 );
gc.drawPolygon(
new   int [] { 100 100 120 120 140 100 });

我们演示下通过设置背景色和绘制一些附加的填充好的矩形的样子。值得注意的是填充好的矩形通常不能精确覆盖到绘制的边框。

gc.setBackground(red);
gc.fillRectangle(
20 100 20 20 );
gc.fillRectangle(
50 100 20 20 );
gc.drawRectangle(
50 100 20 20 );

另一个功能是绘制文本。drawString()方法就是为这个目的诞生的。

gc.drawString( " Text " 120 20 );

我们还可以设置裁剪区域,因此我们绘制的区域是可以受到限制的。setClipping方法的用法很像drawRectangle方法,它也是需要4个整形参数(x,y,width,height)。下面的代码是演示了填充一个椭圆区域。

gc.setClipping( 40 60 40 40 );
gc.fillOval(
30 50 30 25 );

还有很重要的一步要说明,因为SWT类库中大多数的graphic类用的资源都是操作系统分配的,所以程序员它们必须被明确地释放这些资源。在代码中,当shell关闭的时候释放资源。我们的例子中,GC必须这样释放:

gc.dispose();

 当绘制都结束的时候把这行代码放到最后。当你用完图形对象的时候都应该这样释放它。其他需要明确手动释放的资源还包括Color,Image和Font。你可能已经注意到例子中我们没有释放blue和red的Color对象。那是因为它们是系统指定(分配)的颜色(操作系统自己也要用的咯)。一条普遍的规则是:你创建了,你就应该释放。同样地,如果你没有创建它,那你也就没有责任去释放它。待会儿我们会演示如何创建自定义颜色,当然,它们必须明确被释放。

GC包括大量的图形函数,包括设定实线的宽度和样式,得到文本的特征(大概是显示样式吧,加粗之类的),复制控件的区域内容等。在SWT的javadoc里有详细的说明。

在之前的程序中,还是有很多的暇疵的。如果你最小化窗口或者拖动一下,使得部分canvas被其他窗口挡住,那么那部分就会被擦除。那是因为我们只在程序启动的时候绘制了图形,而且我们不会去重绘图形。
例子:当我们把窗口拖动到屏幕的边缘之后,一部分图像消失了,并且不会重新出现。

为此,我们需要检测什么时候需要重绘canvas,所以我们必须添加一个PaintListener。下一个示例(G C D e m o 2 . j a v a)保留了左上角的矩形,它会实现重绘图形。

注意:很少需要把绘制图形的过程放到PaintListener之外,并且你应该尽量避免放到外面。

canvas.setSize( 150 150 );
canvas.setLocation(
20 20 );
canvas.addPaintListener(
new  PaintListener() {
        
public   void  paintControl(PaintEvent e) {
                GC gc 
= e.gc;
                gc.drawRectangle(
3 5 20 25 );
        }
});
shell.open();

PaintListener会检测何时需要重绘图形,并且会执行paintControl方法来绘制我们的矩形。注意shell.open()在PaintListener添加以后出现,因为初始化绘制要在窗口打开之前做完。如果我们把PantListener放到shell打开之后的画,那么这个矩形在canvas下次需要重绘——被遮挡或最小化之后才出现(绘制)。
同样的,注意我们怎样去得到我们需要的GC对象。PaintEvent包含了对象必须的GC,所以我们只要简单访问它就可以了。这里我们没有必要去释放这个GC对象,因为它不是我们创建的。

上面代码运行后看起来应该和下面的截图差不多。最小化,移动,拉伸以及类似的操作都没有影响到我们画的矩形,因为当它会自动重绘。

Fonts & Colors
字体和颜色是SWT中另外两种重要的图形组件。两者都是操作系统分配的资源,所以它们如同GC和Display一样必须被明确释放。

在前面的例子中一样,我们用PaintListener实现我们的图形绘制。不同的是这次我们会直接画在shell上,你不需要画在Canvas控件上了。

shell.addPaintListener(new PaintListener() {
        public void paintControl(PaintEvent e) {
                GC gc = e.gc;
                Display d = e.widget.getDisplay();

我们用Color的构造函数来创建一个新的颜色,需要指定Display以及红、绿、蓝三个颜色分量。每个颜色分量应在整数0~255之间取值。我们自己创建了颜色,那就必须释放它,所以在最后行我们调用了dispose方法。

                Color color = new Color(d, 120, 42, 105);
                gc.setForeground(color);

同样地,我们通过Font的构造函数并设置字体名称、大小以及字体风格来创建一个新的字体。字体的样式要通过按位“或”运算SWT.BOLD,SWT.NORMAL和SWT.ITALIC来设置。一旦创建了一个字体,我们就可以调用GC的setFont方法把这个字体当参数传入。当setFont方法调用后,绘制的文本就会按照我们创建的字体样式显示。就像Color一样,一个Font在用完后也要释放,所以我们调用font.dispose()。

                Font font = new Font(d, "Arial", 24, SWT.BOLD |  SWT.ITALIC);
                gc.setFont(font);

我们通过GC的drawString()方法绘制自定义字体和颜色的文本。

                gc.drawString("Text", 20, 20);

同样,我们还需要释放我们的图形对象。但是GC除外,因为那不是我们创建的。

                color.dispose();
                font.dispose();
        }
});
shell.open();

需要注意的是我们在添加完监听后才调用的shell.open(),所以初始化后就显示我们绘制的结果了。运行后应该和下面的截图差不多。


Images
图像对象允许你的程序载入、绘制以及显示一个图像(翻译了很拗口 = =!)可以打开的文件类型包括:BMP (Windows Bitmap),ICO (Windows Icon),JPEG,GIF以及PNG。下面是用到Image对象的简单的例子(ImageDemo.java)。它会从文件中载入一个图像然后绘制到shell上。

首先我们设定一个shell:

Display display  =   new  Display();
Shell shell 
=   new  Shell(display);
shell.setSize(
140 , 100 );

接下来我们从文件中载入一个图像,然后打开shell并绘制到窗口上。通过把display和图形文件路径传入Image的构造参数创建Image对象。注意我们马上就打开shell,因为在我们绘制之前需要打开shell。(同样,注意我们在打开shell之前载入图像。如果我们交换下面的两行代码,那么可能会看到在shell打开的和图像绘制到上面之间会有一点点小小的延迟,因为从文件中载入图像是比较费时间的。Soso said:文件I/O费时间,地球人都知道)

Image img  =   new  Image(display,  " EclipseBannerPic.jpg " );
shell.open();

现在我们需要该shell图形环境变量(或者叫GC)来绘制图像。要把载入的图像画到shell上,我们要调用gc的drawImage()方法。第一个参数是要绘制的图像对象,后面的两个参数是图像显示在shell上的的x和y坐标。

GC gc  =   new  GC(shell);
gc.drawImage(img, 
0 0 );

当都设置好以后就还剩最重要的一个命令了。

gc.dispose();

销毁了image对象后释放它占用的资源。这是至关重要的!因为图像相对来说是比较消耗操作系统资源的重量级对象。操作系统一次能分配的图形资源是有限的,所以当你不用的时候把它释放并把资源还给操作系统就显得很重要了。下面是结果的截图。

 

注意,如果你最小化或者缩小窗口,那么不可见部分就会被擦除,并且不会重绘。因为我们只绘制了一次,并没有提供重绘的方法,不过你可以自己去实现重绘。

JAR Files
如果你想把你的图像文件打包成一个JAR文件,那么想要成功载入这些这些图像文件的话就需要对代码做一些修改了。因为现在的图像文件是在一个JAR文件中,Image的构造函数不能再直接通过文件系统来访问到图像文件。你必须像下面代码演示的一样来创建图像对象:

InputStream  is   =  getClass().getResourceAsStream( " EclipseBannerPic.jpg " );
Image img 
=   new  Image(display,  is );

在ImageDemo.java代码里面,你可以用这代码替换掉,它会运行了很好的。如果图像文件是打包在JAR文件里面的,并且不是在根目录里面(也就是说是在JAR文件的一个子目录里面),那你就要指定具体路径。举个例子,图像文件是在JAR文件的Images目录里面,那你就要这样写代码:

getResourceAsStream(“images/EclipseBannerPic.jpg”)

上面代码的第一行从JAR文件中的图像文件得到了一个InputStream对象。这是文件的字节流表示方式。getClass()返回一个Class类型的对象给当前的类。Class类包含一个getResourceAsStream方法——可以用来载入资源并返回资源的字节流对象。
返回的InputStream对象是用在Image对象的另一个构造函数上的。

还有一些其他的游泳的Image的构造函数:
Image(Device device, int width, int height)是用来创建一个空白的图像。
Image(Device device, Rectangle bounds)和上面一样,只不过把宽和高用一个矩形对象来代替了。

空白图像的用途最多就是用来在那上面用GC.drawImae()再内存中快速绘制图形。在我们直接在屏幕上绘制图形的时候有效防止闪烁现象。

在OffscreenImageDemo.java中,我们创建了一个空白的图象,然后得到它的GC。我们用这个GC在内存中画一条实线,然后再创建一个shell的GC,再通过shell的GC把刚才的Image对象画到屏幕上。

Image img  =   new  Image(display,  50 , 50 );
shell.open();
GC imgGC 
=   new  GC(img);
imgGC.drawLine(
10 20 30 40 );
GC shellGC 
=   new  GC(shell);
shellGC.drawImage(img, 
0 0 );

在这个例子中也许感觉不到直接用shell的gc绘制到屏幕上会有什么区别,因为画一根实线对现在的计算机来说实在是雕虫小技。但是如果我们是要绘制非常复杂的图形动画的话,那么通过内存绘制能显著得减少闪烁现象!(同理:地球人都知道,I/O是最耗时的)

在下面的例子里(ImageButtonDemo.java),我们演示了如何将图像显示到按钮上去。这其实很简单,只需要载入或者绘制好图形,然后通过Button的setImage方法将图像作为参数传入就可以了。
我们用这个例子来实现一点不同的效果,复制给出的图像。

Image(Device device, Image srcImage, int flag)是用来创建一个srcImage的副本,然后通过flag来改变点时觉效果。flag是SWT.IMAGE_COPYSWT.IMAGE_GRAYSWT.IMAGE_DISABLE中的一个。

和上面说过的一样,我们先载入一个图像。

Image original  =   new  Image(display,  " EclipseBannerPic.jpg " );

现在我们来创建三个它的拷贝,每个都用不同的flag值,看下效果。

Image copy  =   new  Image(display, original, SWT.IMAGE_COPY);
Image gray 
=   new  Image(display, original, SWT.IMAGE_GRAY);
Image disable 
=   new  Image(display, original, SWT.IMAGE_DISABLE);

我们初始化三个按钮(b1,b2和b3),并设置它们的尺寸和位置。(这里省略了很多我们已经重复教过很多次的代码)接下来的重要的程序块实际上设置了按钮上的图象:

b1.setImage(copy);
b2.setImage(gray);
b3.setImage(disable);

最顶上的按钮(b1)显示了一个未加修改的原始图像。第二个按钮(b2)显示了一个只有灰度版本的图像。最下面的按钮(b3)现实了一个“无效”版本的图像(灰白并且有点褪色的)。


注意,如果你通过setText方法设置了文本内容,那么这个文本会替代图像。标准的按钮控件不能同时显示文本和图像。

同样地,除非你要对图像进行点改变,不然用SWT.IMAGE_COPY来复制一个图像是没什么好的效果的。也不要想在那从中销毁原始的图像。另一方面,我们也可以把原始的图像用到很多不同的地方去,它并不会影响什么性能。减少图像的载入就是减少对内存以及操作系统资源的消耗。

我们还可以把图像添加到菜单的内部对象上去。在ImageMenuDemo.java中将会载入“arrow.bmp”并在MenuItem的文本左侧显示。

我们先创建只有一个内容的菜单。

Menu bar = new Menu(shell, SWT.BAR);
shell.setMenuBar(bar);
MenuItem file = new MenuItem(bar, SWT.CASCADE);
file.setText("File");
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
MenuItem action = new MenuItem(fileMenu, SWT.PUSH);
file.setMenu(fileMenu);
action.setText("Action");

然后我们载入一个图像,并设置到菜单上。

Image icon = new Image(display, "arrow.bmp");
action.setImage(icon);

下面是运行结果:

设置图像(就像我们给按钮和菜单内部对象做的)也可以用在很多其他不同的控件上,比如TreeItem、TableItem、ToolItem等等。

Splash Screens
这个例子(SplashDemo.java)演示了如何生成一个简单的没有边框效果的程序。其实就是创建shell的时候指定SWT.NO_TRIM样式。这样会产生一个没有窗口边框的shell,我们这里就直接放置一个图片上去。然后我们设置一个线程,让它先停止几秒钟,然后处理掉这个Splash Screens。
这个程序稍稍和其他标准的程序有点不同,所以这里列出了整个runDemo()方法。
首先我们创建一个display,然后是shell。创建shell唯一不同就是用了NO_TRIM样式。

Display display = new Display();
Shell shell = new Shell(display, SWT.NO_TRIM);

然后载入想要显示的图像,然后得到一个包含图像信息的ImageData对象。它是经常用来设置shell适合图像的大小。

Image image = new Image(display, "splash.jpg");
ImageData imdata = image.getImageData();
shell.setSize(imdata.width, imdata.height);

在接下来的几行里,首先调用了display的getBounds()方法,它指明了屏幕的大小。然后取得宽和高的值,减去图片的宽和高。最后分别除以2得到图片居中的位置坐标。

Rectangle r = display.getBounds();
int shellX = (r.width - imdata.width) / 2;
int shellY = (r.height - imdata.height) / 2;
shell.setLocation(shellX, shellY);

打开shell,在上面创建一个gc。设置好shell的大小以后设置gc.drawImage(image, 0, 0)使得图片充满shell。

shell.open();
GC gc = new GC(shell);
gc.drawImage(image, 0, 0);

图片一旦显示在屏幕上,我们简单暂停几秒钟。通过把当前线程睡眠几秒钟产生一个暂停效果。Thread.sleep(int)的静态方法就可以做到。

final int SLEEP_TIME = 4000;
try {
 Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
}

最后,我们释放所有资源。

image.dispose();
shell.dispose();
display.dispose();

运行程序时应该可以看到一个图片漂浮在屏幕中间,呵呵。

splash窗口出现时程序会暂停。一个很好的用处就是在一个耗时的载入任务是显示一个splash窗口。在Eclipse.org的网站上有演示这样的方法的小代码。它用一个进度条(progress bar)显示离程序结束还有多少进度。

Advanced Widgets Project
为了可以运行上面讲到的程序,把它放到一个项目中。当程序运行的时候会显示以下窗口,点击相关的按钮就会运行各自的程序。

搞定~Advance剩下最后一篇了~

 不知道这个内容词语过滤是谁做的!太不厚到了!郁闷,察了2个多小时才发现是“( G C D e m o 2 . j a v a )”这几个字不能打!怎么过滤的……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值