摘要:org.eclipse.swt.graphics包(package),包含了管理圖形資源的類。只要實(shí)現(xiàn)了org.eclipse.swt.graphics.Drawable接口,就可在上面繪畫,包括 org.eclipse.swt.widgets.Control 和 org.eclipse.swt.graphics.Image 。 org.eclipse.swt.graphics.GC封裝了全部繪畫API,包括如何繪制線條、圖形、如何繪制文本、圖像以及填充圖形。 本篇將展示如何使用GC在圖像上繪畫, 控件的繪畫事件(paintEvent)回調(diào)。畫布(Canvas)控件,因?yàn)椴煌睦L畫操作,擁有很多構(gòu)造風(fēng)格常量允許你指定何時(shí)以及如何產(chǎn)生繪畫,本篇也將展示這些東西。
英文原文:Graphics Context - Quick on the draw http://www./articles/Article-SWT-graphics/SWT_graphics.html 目錄:
SWT圖形系統(tǒng)使用了與控件(Control)相同的坐標(biāo)習(xí)慣,即客戶區(qū)最左上角開始的點(diǎn)為原點(diǎn)(0,0),x軸坐標(biāo)向右增加,y軸坐標(biāo)向下增加。Point 類被用于描述位置(坐標(biāo)系統(tǒng)中的位置)以及偏移量(SWT中并沒有Dimension類,矩形(rectangle)的大小是由Point類捕獲的x、y坐標(biāo)點(diǎn)偏移量到原點(diǎn)坐標(biāo)來描述的)。 圖形上下文(Graphics Context)圖形能夠被畫在任何實(shí)現(xiàn)了org.eclipse.swt.graphics.Drawable接口的東西上,比如控件(Control)、圖像(Image)、顯示設(shè)備或者打印設(shè)備。 org.eclipse.swt.graphics.GC 就是封裝了執(zhí)行繪畫操作的圖形上下文(graphics context)。一般有兩種使用GC的方法:一種是讓Drawable 實(shí)例作為GC構(gòu)造函數(shù)的參數(shù)獲取的GC,另外一種是繪畫事件(paintEvent)回調(diào)提供的GC。 在圖像(Image)上繪畫以下代碼是把一個(gè)圖像作為構(gòu)造參數(shù)獲取圖像的GC,然后在它上面繪制線條。![]() ![]()
在控件(Control)上繪畫org.eclipse.swt.widgets.Control實(shí)現(xiàn)了Drawable接口,所以你可以在控件(Control)上繪畫,圖像(Image)上繪畫方法與控件相同(把控件或圖像作為參數(shù)傳給GC獲取控件或圖像的GC,然后在其上進(jìn)行繪畫)。但是,圖像(Image)上的繪畫與控件有所不同是圖像的修改是永恒不變的。如果使用GC在控件上進(jìn)行繪畫,操作系統(tǒng)自身在繪制控件時(shí)會(huì)覆蓋你所做的繪畫操作。正確的方法的是為控件添加一個(gè)繪畫監(jiān)聽器,這個(gè)監(jiān)聽器類就是org.eclipse.swt.events.PaintListener,然后在監(jiān)聽器中回調(diào)方法參數(shù)就是org.eclipse.swt.events.PaintEvent的一個(gè)實(shí)例。PaintEvent 包含了一GC,這樣在控件上面或者是指定區(qū)域里面進(jìn)行繪畫的環(huán)境就準(zhǔn)備好了
以下代碼
雖然Shell的大小設(shè)置為(150,150), 但實(shí)際上可繪畫的區(qū)域比這還要再小一些,因?yàn)镾hell還包括了邊框、工具欄以及菜單欄,這也就是我們所要了解的客戶區(qū)域。任何面板(Composite)都是使用getClientArea()方法獲取客戶區(qū)域的。 因?yàn)閼?yīng)用程序總是在底層OS繪制完控件后才得到繪畫事件,所以繪畫事件中的GC進(jìn)行繪畫后的效果就可以最終顯示在控件上面了。當(dāng)然也有例外,比如工具欄區(qū)域就不能在上面進(jìn)行繪畫。
剪切(Clipping)
這段代碼在Shell上畫了一個(gè)三角形。
當(dāng)控件發(fā)生繪畫事件,GC總是剪切需要重繪的那部分區(qū)域。例如,另一個(gè)窗口移到了一個(gè)SWT shell的前面,隨后又移走。那么需要重新顯示的就是GUI被損壞的那部分(shell被覆蓋的那部分),一個(gè)繪畫事件就是事件隊(duì)列。當(dāng)繪畫事件發(fā)生,
在上面的例子中每當(dāng) paintControl(PaintEvent)被調(diào)用的時(shí)候, 就將在PaintEvent's area中尋找一個(gè)優(yōu)化。繪畫事件(paint event)很可能不交叉在繪畫的形狀(shape)中,在這種情況下,就不需要繪畫(painting)或者指使需要一部分重畫而已。依靠繪畫的類型,就可以解決GC所選擇的繪畫部分,但事實(shí)上這要比GC剪切花費(fèi)更多開銷,而且在實(shí)踐中常常忽視這些被損壞的區(qū)域讓GC重新繪畫全部,只有在刷新操作中才會(huì)依賴剪切。
如果程序需要手工損壞控件的某部分區(qū)域,可以使用
畫布(Canvas)雖然任何控件都可以通過繪畫事件(paintEvent)在其上進(jìn)行繪制, 但是org.eclipse.swt.widgets. Canvas的默認(rèn)行為是使用當(dāng)前背景色填充自身的整個(gè)客戶區(qū)域。這樣會(huì)引起屏閃,因?yàn)槔L畫事件也是在GC上繪畫,所以用戶就會(huì)看到被填充的原始背 景色和產(chǎn)生繪畫之間的閃爍。有一種方法可避免此類情況,在創(chuàng)建Canvas時(shí)使用SWT.NO_BACKGROUND樣式。這樣就防止了繪畫背景,意思就 是程序要負(fù)責(zé)繪畫客戶區(qū)域的每一個(gè)像素。
當(dāng)部件調(diào)整大小時(shí),客戶區(qū)域會(huì)重復(fù)繪畫,這就會(huì)出現(xiàn)屏幕閃爍。使用SWT.NO_REDRAW_RESIZE 可減少這樣的情況,控件會(huì)減少不必要的重繪。比如改變尺寸大小,繪畫事件GC只會(huì)剪切需要重繪的部分即底部區(qū)域和右邊區(qū)域,就像一個(gè)反方向的“L”。
在固定大小的GC上繪畫NO-REDRAW_RESIZE樣式能很好的減少屏閃。但是錯(cuò)誤的使用NO_REDRAW_RESIZE 可以導(dǎo)致圖形成扁圓形。扁圓形是個(gè)大概的說法,事實(shí)上是指部件沒有隨大小的調(diào)整而進(jìn)行正確的更新。下面的例子就演示了這樣的情況。
canvas的大小被增大,GC只剪切需要重繪的地方,扁圓形狀就產(chǎn)生了。
問題出在
任何SWT部件,如果超過一個(gè)矩形區(qū)域被“損壞”,平臺(tái)會(huì)把它們合并成一個(gè),也就是說SWT程序只能處理一個(gè)繪畫事件。在Canvas上使用NO_MERGE_PAINTS 樣式可以覆蓋這樣的行為,可以為每一個(gè)被“損壞”的矩形區(qū)域調(diào)用繪畫事件監(jiān)聽。
風(fēng)格常量NO_BACKGROUND, NO_REDRAW_RESIZE 以及NO_MERGE_PAINTS
能夠被使用在任何面板(Composite)以及子類中, 包括Canvas、Shell以及Group。
雖然這是被SWT允許的(不會(huì)由異常拋出),但在Composite 類的Javadoc中關(guān)于風(fēng)格有這樣的警告 "... 如果在其他的
另一種減少屏幕閃爍的方法,就是使用雙緩沖技術(shù)。你可以先根據(jù)Canvas客戶區(qū)域大小創(chuàng)建Image對(duì)象,然后使用
繪制線條和形狀(Drawing lines and shapes) GC 擁有很多繪畫線條的方法,比如畫連接兩個(gè)坐標(biāo)點(diǎn)的直線、連接多個(gè)坐標(biāo)點(diǎn)的直線或者是預(yù)先定義好的形狀,線條顏色就是GC的前景色,可以通過風(fēng)格樣式常量來 決定線條的粗細(xì)胖瘦。繪畫事件其GC也有很多相同的屬性(前景色、背景色、以及顏色),并且線條的默認(rèn)寬度是1像素。
畫一條從坐標(biāo)點(diǎn)(x1,y1)到坐標(biāo)點(diǎn)(x2,y2)的直線,如果兩點(diǎn)的坐標(biāo)值相同就相當(dāng)于畫一個(gè)圓點(diǎn)。
|
SWT.LINE_SOLID | ![]() |
SWT.LINE_DOT | ![]() |
SWT.LINE_DASH | ![]() |
SWT.LINE_DASHDOT | ![]() |
SWT.LINE_DASHDOTDOT | ![]() |
GC.setLineWidth(int width);
gc.setLineWidth(2); | ![]() |
gc.setLineWidth(4); | ![]() |
因?yàn)榫€條風(fēng)格影響著所有的繪畫操作,所以這也就使你可以畫出不同邊線風(fēng)格的矩形或橢圓等圖形。
gc.setLineWidth(3);
gc.drawOval(5,5,40,40);
gc.setLineWidth(1);
gc.setLineStyle(SWT.LINE_DOT);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
gc.drawRectangle(60,5,60,40);
當(dāng)GC的屬性被改變,比如像線條的寬度、線條的風(fēng)格或者是顏色,這些變化都會(huì)影響到后續(xù)的繪畫操作。以上代碼片段中,首先設(shè)置線條的寬度為3畫了一個(gè)橢圓,隨后重新設(shè)置線條屬性畫了一個(gè)邊線是虛線的矩形。在SWT圖形編程中,忘記重新設(shè)置這些字段屬性的值是經(jīng)常會(huì)犯的錯(cuò)誤。
繪制文本(Drawing text)
GC之上同樣可以繪制文本,文字的輪廓可通過GC的foreground
color和font確定。你需要定義它的左上角坐標(biāo)(就是字體的位置)以及字體的高和寬。繪制文本有兩種設(shè)置方法:第一種是在drawText()繪制
方法里直接輸入文本它將處理行分隔符和tabs制表符,常用來模仿一個(gè)Label。第二種是在drawString()繪制方法中輸入字符串,沒有tab
以及回車處理,常用于更加復(fù)雜的控件,就像StyledText常用于
Eclipse Java editor那樣。
GC.drawText(String text, int x, int y);
Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);
// ...
gc.drawText("Hello World",5,5);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
gc.setFont(font);
gc.drawText("Hello"tThere"nWide"tWorld",5,25);
// ...
font.dispose();
drawText API 支持控制轉(zhuǎn)義字符,"t 就是tab,"n就是回車換行。
GC.drawString(String text, int x, int y);
Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);
// ...
gc.drawString("Hello World",5,5);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
gc.setFont(font);
gc.drawString("Hello"tThere"nWide"tWorld",5,25);
// ...
font.dispose()
使用drawString時(shí),tab 和回車換行轉(zhuǎn)義字符沒有被處理。在GC繪制時(shí)字符串所占的大小基于它的內(nèi)容和GC所設(shè)置的字體。獲取字符串所占寬度可以分別使用GC.stringExtent(String text)和
GC.textExtent(String text)這兩個(gè)方法。 它們所返回的Point的x,y坐標(biāo)值分別就是寬和高。
GC.drawText(String text, int x, int y, boolean isTransparent);
drawText(String text, int x, int y)繪制的文本使用的是GC的當(dāng)前foreground color。當(dāng)你希望文本透過背景色在最頂層顯示的畫,你可設(shè)置它的isTransparent這個(gè)布爾型的參數(shù)為true。此方法在圖像(image) 上繪制時(shí)特別有用。
Font font = new Font(display,"Arial",12,SWT.BOLD | SWT.ITALIC);
Image image = new Image(display,"C:/devEclipse_02/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");
GC gc = new GC(image);
gc.drawText("Hello World",5,5);
gc.setFont(font);
gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
gc.drawText("Hello World",5,25,true);
gc.dispose();
image.dispose();
font.dispose();
GC.drawText(String text, int x, int y, int flags);
gc.drawImage(image,0,0);
gc.drawText("Hello"t&There"nWide"tWorld",5,5,SWT.DRAW_TRANSPARENT);
gc.drawText("Hello"t&There"nWide"tWorld",5,25,SWT.DRAW_DELIMITER | SWT.DRAW_TAB | SWT.DRAW_MNEMONIC );
GC.fillPolygon(int[]);
gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
gc.fillPolygon(new int[] { 25,5,45,45,5,45 })

GC.fillRectangle(int x, int y, int width, int height);
gc.fillRectangle(5,5,90,45);
填充矩形的時(shí)候,底部邊線和右邊線是不包含在內(nèi)的。雖然點(diǎn)(5,5)被包含在填充矩形的代碼中,但右下角點(diǎn) 95,50 (5+90 , 45+5) 不在填充區(qū)域的范圍里,右下角的填充點(diǎn)是94,49。這不同于drawRectangle(5,5,90,45),drawRectangle指的是整個(gè)形狀,所以其右下角點(diǎn)是
95,50。
舉例說明一下,下面的代碼填充了一個(gè)矩形,但是填充的顏色并沒有覆蓋邊線。填充區(qū)域的右上角點(diǎn)坐標(biāo)以及寬和高都減小了1像素。
gc.drawRectangle(5,5,90,45);
gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));
gc.fillRectangle(6,6,89,44);
GC.fillRoundedRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight);
gc.fillRoundRectangle(5,5,90,45,25,15);
有點(diǎn)像 GC.fillRectangle(...)方法。底部邊框和右邊框都被排除在填充范圍之內(nèi),所以底部右下角坐標(biāo)變成了(94,49)而不是(95,50)
GC.fillOval(int x, int y, int width, int height);
gc.fillOval(5,5,90,45);
與其他的填充APIs相似
GC.fillArc(int x, int y, int widt4h., int height, int startAngle, int endAngle);
gc.fillArc(5,5,90,45,90,200);
fillArc(...)
方法中的參數(shù)和drawArc(...)中的參數(shù)很相似。fillArc(...)遵守著和其他填充方法一樣的模式,底部邊框和右邊框不在填充范圍之內(nèi)。
GC.fillGradientRectangle(int x, int y, int width. int height, vertical boolean);
對(duì)矩形進(jìn)行由前景色到背景色的漸變填充。Vertical為true表示垂直漸變,反之則表示水平漸變。
gc.setBackgrouind(display,getSystemColor(SWT.COLOR_BLUE));
gc.fillGradientRectangle(5,5,90,45,false);
水平漸變從左邊的黑色前景色開始向右邊藍(lán)色背景色變化。正如其他的填充方法,底部和右邊框是被排除在外的,所以底部右下角會(huì)由1像素插入。
gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
gc.setForeground(display.getSystemColor(SWT.COLOR_CYAN));
gc.fillGradientRectangle(5,5,90,45,true);
垂直漸變從上而下,由前景色向背景色變化。
shell.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
// ...
gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));
gc.fillRectangle(5,5,90,45);
gc.setXORMode(true);
gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
gc.fillRectangle(20,20,50,50);
gc.setBackground(display.getSystemColor(SWT.COLOR_RED));
gc.fillOval(80,20,50,50);
被填充的背景色是白色的(255,255,255)矩形,當(dāng)在上面覆蓋一層藍(lán)色(0,0,255),或(XOR)后的顏色就是黃色(25,255,0)。白背景的部分和黃色異或后就成了黑色(0,0,0)。一個(gè)紅色背景的圓,它覆蓋在藍(lán)色上面異或(XOR)后就成了紫色(255,0,255)。蓋在白色上面異或(XOR)后就成了青色(0,255,255)。
(譯者注: SWT API幫助文檔中對(duì)setXORMode()方法是這樣描述的“此方法在某些平臺(tái)下是不被支持的,顯著表現(xiàn)的有Mac OS X,如果你希望你的代碼可運(yùn)行在所有平臺(tái),應(yīng)該盡量避免使用此方法?!保?/span>
org.eclipse.swt.graphics.Image
表示一個(gè)已經(jīng)準(zhǔn)備好在顯示設(shè)備或打印設(shè)備上顯示的圖像。創(chuàng)建一個(gè)image對(duì)象最簡(jiǎn)單的方式就是從經(jīng)過驗(yàn)證的文件格式中加載文件。支持的文件格式有
GIF, BMP (Windows 位圖), JPG, PNG, 新的Eclipse releases版還支持TIFF格式。
Image image = new Image(display,"C:/eclipse/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");
GC.drawImage(Image image, int x, int y);
eclipse_lg.gif的大小就是
115,164 ,可以使用image.getBounds()獲取。當(dāng)繪畫了一個(gè)圖像,此圖像就會(huì)以它自身范圍的寬度和高度顯示出來。
gc.drawImage(image,5,5);
GC.drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight);
根據(jù)原始圖像的寬度和高度,不但可以繪畫出不同大小的圖像還可以只繪畫原始圖像的局部。
src參數(shù)聯(lián)系圖像本身,要畫完整的圖像,srcX、srcY使用0,0,寬高就使用圖像的寬高。dst參數(shù)表示圖像被畫在哪里以及畫成多大。原始圖像的大小是115,164,若要把圖像寬度增加2倍,高度減低一半,可以使用下面的語(yǔ)句:
gc.drawImage(image,0,0,115,164,5,5,230,82);
使用src坐標(biāo)可以使你只畫出圖像的局部。例如,如果你只想畫出圖像的右上角部分,你可以設(shè)定src坐標(biāo)為20,0,寬度和高度為95,82。下面的代碼中dst寬度和高度同樣使用95,82。通過指定不同大小就可以對(duì)圖像進(jìn)行拉長(zhǎng)或收縮操作。
gc.drawImage(image,20,0,95,82,5,5,95,82);
還能完成一些其他的圖像效果,比如圖像透明度、animation以及alpha通道。但這些不屬于本篇的討論范圍,我希望在以后的文章中能夠涉及到這些東西。
|