技术开发 频道

SWT 全接触

  用SWT绘图通常由两种方法,一种是借助Graphics Context,另一种是利用Draw2D。然而Draw2D是一个基于SWT Composite的轻量级组件,于是在效率上,它无法体现出SWT的Native Code的速度优势。故其虽然强大,但仅适用于绘图工作不是系统瓶颈的应用程序。所以我在这里只介绍第一种方法。

  3.1 Graphics Context

  我们可以在任何实现了org.eclipse.swt.graphics.Drawable接口的类上绘制图形,这包括一个控件,一幅图像,一个显示设备或一个打印设备。类org.eclipse.swt.graphics.GC是一个封装了所有可执行的绘图操作的图形上下文(Graphics Context)。两种使用GC的方式我们已经在本节前言中提过,稍后会作详细说明。

  3.2 在一幅图像上绘制图形

  下面一段代码创建了一个带有图像的GC并在上面绘制了两条线:

1 Image image = new Image(display,"C:/music.gif");
2 GC gc = new GC(image);
3 Rectangle bounds = image.getBounds();
4 gc.drawLine(0,0,bounds.width,bounds.height);
5 gc.drawLine(0,bounds.height,bounds.width,0);
6 gc.dispose();
7 image.dispose();
8

 

  一旦你创建了一个GC,你就有责任通过它的dispose方法释放它的资源。一个由应用程序创建的GC需要立即被绘制,然后尽快释放掉。这是因为每个GC都需要一个底层的系统资源,而在某些操作系统中这些资源是稀缺的,像Win98就只允许同时创建五个GC对象。

  3.3 在Control上绘图

  类org.eclipse.swt.widgets.Control是可绘制的,所以你可以用像在图像上一样的方式来绘制图形。而和在图像上绘制所不同的是,如果你使用GC在一个Control上绘制图形,你需要知道当操作系统自身要绘制这个control的时候,它将覆盖掉你的改动。所以在一个Control上绘制图形的正确方法是加入其绘制事件的监听器。监听器类为org.eclipse.swt.events.PaintListener,其回调函数的参数是一个org.eclipse.swt.events.PaintEvent类的实例。这个PaintEvent实例中包含一个GC的引用,你可以向这个GC发送消息。下面的代码示例说明了如何建立这种类型的绘图:

1 Shell shell = new Shell(display);
2 shell.addPaintListener(new PaintListener(){
3         public void paintControl(PaintEvent e){
4             Rectangle clientArea = shell.getClientArea();
5             e.gc.drawLine(0,0,clientArea.width,clientArea.height);
6         }
7     });
8 shell.setSize(150,150)
9

 

  3.4 剪切(Clipping)

  GC的剪切域是可见绘图发生的部分。在缺省情况下,一个GC是一个被构造的可视部分边界。改变一个GC的剪切域可以让我们构造出各种图形效果。其中的一个例子是如果你想填充一个缺失了边缘的矩形。一种方法是绘制多边形矩形来组成所需要的图形,另一种方法就是剪切GC,然后对其剪切部分进行填充。

1 shell.addPaintListener(new PaintListener() {
2         public void paintControl(PaintEvent e) {
3             Rectangle clientArea = shell.getClientArea();
4             int width = clientArea.width;
5             int height = clientArea.height;
6             e.gc.setClipping(20,20,width - 40, height - 40);
7             e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
8             e.gc.fillPolygon(new int[] {0,0,width,0,width/2,height});
9         }
10     });
11

 

  这段代码在Shell上的显示的过程效果如下:

        3.5 画板(Canvas)

  虽然任何Control都可以通过自身的paintEvent来绘制图形,但其子类org.eclipse.swt.widgets.Canvas是专门被设计用来进行图形操作的特殊的绘图类。我们既可以使用一个Canvas,再加入一个绘图监听器来实现绘图,也可以通过继承来建立一个可重用的自定义Control。Canvas有很多style bit,可以在绘图发生时产生作用。

  3.6 绘制直线和图形

  我们有很多方法可以在一个GC上画线,包括在两点之间,一系列离散的点之间或一个预定义的图形上都可以。直线是以GC的前景色来绘制的,我们可以通过GC绘制拥有不同厚度的各式直线。对于一个Paint事件,GC有着与Control组件一样的属性,即激发事件且缺省的直线样式固定为1个像素宽。

  GC.drawLine(int x1, int y1, int x2, int y2);这条语句在可绘制的面板上的两点间花了一条直线,起始点为(x1,y1),终止点为(x2,y2)。终止点包含在画好的直线中。如果起始点等于终止点的话,将会有一个独立的象素点被绘制出来。

  GC.drawPolyline(int[] pointArray);这条语句绘制了一系列互相连接的线段,作为参数的数组用于描述点的位置。语句gc.drawPolyline(new int[] { 25,5,45,45,5,45 });绘制了如下的图形:

  GC.drawPolygon(int[] pointArray);与drawPolyline(int[])是类似的,唯一区别在于最后一个点和低一个点是连接的。gc.drawPolygon(new int[] { 25,5,45,45,5,45 });将会获得与上图一样的结果。

  GC.drawRectangle(int x, int y, int width, int height);这条语句从左上角的(X,Y)点,用参数中的宽和高画出了一个矩形。gc.drawRectangle(5,5,90,45);将会绘制出如下图形:

  GC.drawRoundedRectangle(int x,int y,int width,int height,int arcWidth,int arcHeight);一个圆矩形与标准矩形的区别就在于其四个角是圆的。圆矩形的每一个角都可以被想象成为1/4个椭圆,并且arcWidth和arcHeight由完整的椭圆的宽和高决定。gc.drawRoundedRectangle(5,5,90,45,25,15);绘制了一个左上角位置为5.5的圆矩形,右边的图形是放大后的效果:

  GC.drawOval(int x, int y, int width, int height);一个椭圆是由其相对应的矩形的左上角的位置(x,y)来确定绘制位置的,其宽和高即为对应矩形的宽和高。对于圆形来说,只需要另宽和高相等即可。

  GC.drawArc(int x, int y, int width, int height, int startAngle, int endAngle);曲线的绘制也是与一个相应的矩形有关,即其左上角的位置与宽和高都是相应矩形的属性。StartAntle是从横向的X开始计算的,所以0度指向的是东而不是北。曲线的绘制是从StartAngle到endAngle以逆时针方向执行。gc.drawArc(5,5,90,45,90,200);所绘制的图形如下:

  GC.setLineStyle(int style);可以设置所绘制曲线的样式,下面列出了一些曲线样式常量(在org.eclipse.swt.SWT中定义)和与之对应的曲线的图像:

  GC.setLineWidth(int width);可以用于指定所要绘制的曲线的宽度。缺省情况下的曲线宽度为1个像素。

  由于直线的样式和宽度挥作用到所有的绘图操作上,所以我们可以作出如点矩形或粗线椭圆这样的图形:

  3.7 绘制文本

  文本可以被绘制在一个GC上, 字形是用GC的前景色和字体来绘制的,并且它所占用的区域是用GC背景色绘制的。要绘制文本,你需要定义要绘制文本的左上角,宽度和高度。有两组方法可以用来绘制文本,第一组方法的名字里都带有一个Text,并将会处理直线定界符和制表符。第二组API方法集的名字里都带有String,它们没有制表符或回车的处理,并主要用于控制像Eclipse的Java编辑器StyledText这样复杂的Control。

1 GC.drawText(String text, int x, int y);
2 Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);
3 // ...
4 gc.drawText("Hello World",5,5);
5 gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
6 gc.setFont(font);
7 gc.drawText("Hello\tThere\nWide\tWorld",5,25);
8 // ...
9 font.dispose();
10

 

  drawText API将控制字符\t处理为制表符,将\n处理为回车符。

1 GC.drawString(String text, int x, int y);
2 Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);
3 // ...
4 gc.drawString("Hello World",5,5);
5 gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
6 gc.setFont(font);
7 gc.drawString("Hello\tThere\nWide\tWorld",5,25);
8 // ...
9 font.dispose()
10

 

  当使用drawString时,制表符和回车符将不会被处理。

  在一个GC上绘制字符的时候,一个字符串所占用的大小取决于它的内容以及GC的字体。想要确定一个字符串在被绘制之后所占用的区域可以使用方法:GC.stringExtent(String text), 或 GC.textExtent(String text)。这两个方法都返回一个Point类,这个Point的X和Y是渲染参数字符串所需要的宽和高。

  3.8 图形填充

  直线是用GC前景色绘制的,而图形的填充用的是GC的背景色。

1 GC.fillPolygon(int[]);
2 gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
3 gc.fillPolygon(new int[] { 25,5,45,45,5,45 })
4
5 GC.fillRectangle(int x, int y, int width, int height);
6 gc.fillRectangle(5,5,90,45);
7

         需要注意的是,当一个矩形被填充的时候,右面和下面的边缘是不被包括在内的。

1 GC.fillRoundedRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);
2 gc.fillRoundRectangle(5,5,90,45,25,15);
3

  像GC.fillRectangle(...)方法一样,右面和下面的边缘不被包含在内,于是右下角的坐标为(94,49)而不是(95,50)。

1 GC.fillOval(int x, int y, int width, int height);
2 gc.fillOval(5,5,90,45);
3

1 GC.fillArc(int x, int y, int widt4h., int height, int startAngle, int endAngle);
2 gc.fillArc(5,5,90,45,90,200);
3
4
5

  fillArc()的参数和drawArc()的参数是类似的,偏移量是从右面的轴开始填充,然后沿逆时针方向旋转给定的角度(endAngle-startAngle)。

  GC.fillGradientRectangle(int x, int y, int width. int height, vertical boolean);

  这个方法让我们可以指定图形在填充时所用的颜色可以从GC的前景色按梯度变化(渐变)到背景色。梯度既可以是横向的也可以是纵向的。

1 gc.setBackgrouind(display,getSystemColor(SWT.COLOR_BLUE));
2 gc.fillGradientRectangle(5,5,90,45,false);
3

  上面两条语句建立了一个使用黑色背景的从左至右的横向梯度填充。和其他填充方法一样,左面和下面的边缘不被包括在内,所以由下角的位置缩小一个像素。

1 gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
2 gc.setForeground(display.getSystemColor(SWT.COLOR_CYAN));
3 gc.fillGradientRectangle(5,5,90,45,true);
4

  上面这3行代码的含义为在纵向自顶向下用前cyan(景色)开始,并以蓝色(背景色)结束的填充。

        3.9 异或(XOR)

        如果你设置了GC的XOR模式为true的话,将会发生如下情况:对于每一个像素点,原来被显示的红,绿,蓝的值将被已存在的红,绿,蓝色进行异或操作,所得结果既作为新的目标像素。

1 shell.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
2 // ...
3 gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
4 gc.fillRectangle(5,5,90,45);
5 gc.setXORMode(true);
6 gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
7 gc.fillRectangle(20,20,50,50);
8 gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
9 gc.fillOval(80,20,50,50);
10

 

        3.10 绘制图像(Draw Image)

        类org.eclipse.swt.graphics.Image被用来表示准备要在像打印机,显示器这样的设备上显示的图形。建立一个图像最简单的方法就是从组织好的文件格式中装载它。SWT所支持的图像格式有:GIF,BMP,JGP,PNG和TIFF。

1 Image image = new Image(display,"C:/eclipse_lg.gif");
2 GC.drawImage(Image image, int x, int y);
3

        每幅图像都有用其边界决定的尺寸。例如,图象eclipse_lg.gif的大小为115*164,我们可以通过image.getBounds()方法来进行设定。当一幅图像被绘制的时候,它将会以自身定义的边界作为显示之后的宽和高。gc.drawImage(image,5,5);

        至此,SWT在2D绘图方面的讲解告一段落,上面所提到的内容涵盖了SWT的大部分绘图功能,并在每个部分都给出了要注意的细节。至于具体实现就要靠各位的聪明才智了。下面让我们进入最后的部分-SWT的3D绘图。

 

0
相关文章