国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

SWT:實現自我繪制的Button組件

2019-11-18 15:27:50
字體:
來源:轉載
供稿:網友

  在所有SWT組件中,Button幾乎是最常用的,其功能在對于一般的情況來說也足夠豐富了。你可以為Button組件設置要顯示在其中的文本或者圖像、設定ToolTip,甚至只要修改一個風格樣式就能得到一個看上去相當不錯的方向箭頭按鈕。

  然而,我對Button組件還是不能感到滿足。最大的遺憾就是:對它的外觀,所能做的工作也就僅限于此了。假如你想讓按鈕擁有一個漂亮的、漸變色的背景和一些非凡的文字效果,怎么辦呢?答案是沒有辦法。Button類里面似乎沒有任何方法提供我想要的功能。

  我曾嘗試過的第一個想法是用Button.addPaintListener來修改按鈕的外觀。但是,結果令人失望——雖然它顯示出來的時候的確按照預想進行繪制了,但是當你用鼠標去按它的時候,馬上又變回了原本灰頭土臉的樣子。顯然,在按下按鈕的時候,它并不是觸發paint事件,而是按照自己的想法畫出原本的按鈕,于是我的工作全部白費了。

  假如嘗試為按鈕設定圖像會怎么樣呢?這也不是一個好主意。首先,不管你選擇什么樣的圖像,都沒辦法去掉按鈕四面的邊框,而正是這些邊框嚴重破壞了圖像的和諧感;其次,假如你的程序有幾十甚至上百個按鈕,為每個按鈕都維護一幅圖像(甚至更多——理論上每個按鈕在普通狀態和被按下、禁用的狀態下,甚至當鼠標移進移出按鈕的時候,都應當顯示不同的圖像)明顯是在浪費系統資源;假如你們的美工聽說需要做幾百個圖片,大概也不會給你好臉色看。此外,圖像有一個嚴重的缺點是:它所擁有的像素數目是固定的,難以隨著界面的放大和縮小同時變化。假如強制進行縮放的話,會出現明顯的鋸齒和失真,最終讓你精心設計的窗口變得慘不忍睹。最好還是放棄這個想法。

  假如以Canvas為基礎,設計一個偽裝的按鈕組件又如何呢?聽起來似乎很不錯,因為采用這種辦法的話,我們對如何繪制組件的表面就有了完整的控制權。不過這也意味著你必須對按鈕的狀態進行手工維護。雖然Button本身是一個很簡單的組件,但是重復去做標準按鈕已經作好的工作似乎還是有點無謂。還有一件事情是應當考慮的:我們知道,JFace中的Action機制可以將標準按鈕、菜單項和工具欄按鈕這三種界面組件納入一個統一的事件處理體系。然而,假如我們從Canvas派生去模擬一個按鈕的話,不論你模擬到多么相似的地步,它究竟不是一個真正的Button,Action也不會給它同等的待遇。也就是說手工制作的按鈕無法和JFace Action體系協同工作——除非你去修改Action的處理方法,讓它去接納新的按鈕對象。這可不是一件輕松的工作。

  假如上面的方法都行不通的話,應當怎么辦呢?我們知道,和Swing這樣的框架不同,SWT中的按鈕其實就是操作系統底層所實現的按鈕(這一點也可以用SPY++或者Winsight32之類的工具證實)。同時我們也知道,操作系統——至少是Windows系統,對按鈕已經提供了自我繪制的機制,這就是所謂的Owner Draw(稱為所有者繪制的原因是因為默認情況下繪制消息是發送給按鈕的父窗口處理的,但是父窗口也可以把這個皮球再踢回給按鈕,讓它自己解決)。在Win32 API中,凡是使用BS_OWNERDRAW風格創建、并且能夠(通過消息反射)響應WS_DRAWITEM消息的按鈕,都可以獲得這種定制的能力。 了解這一點,接下來的任務就是研究Button組件有沒有開放這個接口供我們修改了。對Button組件的源代碼進行粗略的瀏覽后,我發現了如下的方法:

package org.eclipse.swt.widgets;

public class Button extends Control {
 …
 LRESULT wmDrawChild (int wParam, int lParam) {
 if ((style & SWT.ARROW) == 0) return super.wmDrawChild (wParam, lParam);
 DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
 ....
  其中DRAWITEMSTRUCT結構的出現是一個明顯的提示:這里就是WM_DRAWITEM消息的響應函數,很幸運它沒有聲明為final的,只要重載它并提供自己的實現就行了。

  看起來是個小case,實際上也是。不過,還有一處小麻煩需要克服。注重wmDrawChild方法沒有使用任何訪問限定符,這意味著它是package friendly的——同一個包中的對象可以訪問和重載此方法,其他包中的對象就沒有這個權力了。也就是說,要定制按鈕對象,我們新建的對象也需要放在同一個包(org.eclipse.swt.widgets)中。看起來有點像在使用Hack手段,不過為了突破SWT給我們的限制,眼下也只好稍稍將就一下。好在swt的包沒有密封(Sealed),不然我就不得不再次宣稱此路不通了。

  既然障礙已經掃清,接下來我們可以來實現前面的想法了。這里我做了一個決定,在上述包中只加入一個抽象類,目的是把必要的接口暴露出來;至于如何繪制按鈕,則留給具體的按鈕對象根據應用程序的需求來決定。這樣,不管你希望實現Windows xp風格的按鈕、還是卡通風格的按鈕、或是平面樣式的,總之不論什么千奇百怪的風格,只要繼續一個類并重載一個繪制方法就行了,而不必每次都要和 Button類的內部打交道。

  基于這種考慮,實現自繪按鈕的抽象類如下:

package org.eclipse.swt.widgets;

import org.eclipse.swt.internal.win32.*;

public abstract class OwnerDrawButton extends Button
{
 public OwnerDrawButton( Composite parent, int style )
 {
  super( parent, style );

  int osStyle = OS.GetWindowLong( handle, OS.GWL_STYLE );
  osStyle = OS.BS_OWNERDRAW;
  OS.SetWindowLong( handle, OS.GWL_STYLE, osStyle );
 }

 LRESULT wmDrawChild( int wParam, int lParam )
 {
  super.wmDrawChild( wParam, lParam );
  DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT();
  OS.MoveMemory( struct, lParam, DRAWITEMSTRUCT.sizeof );
  ownerDraw( struct );
  return null;
 }

 PRotected abstract void ownerDraw( DRAWITEMSTRUCT dis );
}
  注重這個抽象類所作的工作。在構造函數中,它調用操作系統方法為自己加入了BS_OWNERDRAW風格。假如沒有這一步,那么操作系統將不會把這個按鈕視為自繪的按鈕,也不會向其發送任何繪制消息。接下來是WM_DRAWITEM消息的響應函數。在這個函數中,我們簡單的把必要的繪制參數提取出來,然后調用抽象方法ownerDraw去進行實際的繪制工作。任何從OwnerDrawButton類派生的按鈕對象必須重載此ownerDraw方法,來決定如何繪制自身。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 高雄市| 乌兰浩特市| 福清市| 奇台县| 平凉市| 泗阳县| 江安县| 隆安县| 山东省| 阿克陶县| 双牌县| 庄浪县| 合山市| 临高县| 监利县| 兰西县| 黎川县| 荥阳市| 内黄县| 天水市| 抚州市| 台州市| 抚松县| 邹城市| 陇西县| 庆云县| 沙坪坝区| 临西县| 略阳县| 杭锦后旗| 海口市| 得荣县| 华宁县| 若尔盖县| 西充县| 始兴县| 吐鲁番市| 叙永县| 仁布县| 桂平市| 桂平市|