SWT 自定義控件
現(xiàn)在基于Eclipse的應用越來越多,很多桌面應用都是用Eclipse開發(fā)的。Eclipse提供了一套SWT/JFACE 的控件庫,使得人們開發(fā)界面應用極大的方便。但是,SWT/JFACE的控件庫畢竟有限,在應用開發(fā)是我們不可避免地要自己開發(fā)一些自定義的控件。本文通 過開發(fā)一個顏色列表控件的實例介紹了Eclipse自定義控件開發(fā)中所要用到的技術。
目標讀者必須熟悉Java開發(fā),并且有一定的Eclipse開發(fā)經(jīng)驗。
在Eclipse網(wǎng)站上有一篇相關的文章"Creating Your Own Widgets using SWT",該文介紹了開發(fā)自己控件的很多基本概念、方法,并且通過實例進行了介紹,非常好。但是其所用的實例比較簡單,還有很多控件開發(fā)中所要涉及到的內(nèi) 容,例如鍵盤、鼠標事件的處理,滾動條、焦點的處理等等沒有提及。本文通過開發(fā)一個自定義的顏色列表控件的實例,全面地介紹了自定義控件所涉及的技術。同 時,讀者也可以對該實例進行擴展,實現(xiàn)自己的列表控件。
SWT中提供的標準列表控件非常簡單,只能提供字符串的選擇。我們經(jīng)常需要提供一些圖形列表供用戶選擇,這就需要自己開發(fā)自定義的列表控件。顏色選擇列表是我們常用的一種圖形列表,我們就以此為例進行介紹。以下是我們將要開發(fā)的顏色列表。
我們在開發(fā)自定義控件時主要考慮以下問題:
1、 自定義控件的繪制:通常我們需要自己對控件的形狀或圖案進行繪制;
2、 控件對鍵盤事件的響應:當焦點進入控件,用戶進行鍵盤操作,通過鍵盤對控件進行控制時,我們需要讓控件對用戶的操作進行響應。例如在列表中,用戶會通過上下箭頭改變列表的選擇項;
3、 控件對鼠標事件的響應:當用戶用鼠標選中控件,進行操作時,控件必須作出相應的反應;
4、 控件對焦點事件的響應:當界面焦點進入或移出控件,通常我們需要將控件繪制成得到或失去焦點的形狀。例如,當焦點進入列表時,一般被選中的列表項會有虛框表示選中。
5、 響應TAB鍵:對于一個可操縱的控件,用戶可以用TAB鍵將焦點移入或移出。
6、 響應滾動條事件:當控件有滾動條時,我們需要響應用戶對滾動條的操作,完成對控件的繪制工作。
7、 提供事件監(jiān)聽機制:程序員使用你的控件時通常需要監(jiān)聽控件中發(fā)生的一些事件,這樣當事件發(fā)生時,他們能夠進行相應處理。
8、 提供輔助功能(Accessibility):輔助功能是方便殘障人士使用時必須的,標準控件都會提供相應的支持,我們自定義的控件也不例外。
9、 提供功能接口方便程序員訪問:通常為方便程序員使用時獲取控件中的信息或進行設置,我們需要提供一些接口。
首先我們要開發(fā)的列表控件是一個基本控件,所以我們選擇Canvas作為我們開發(fā)的基類。
public class ColorList extends Canvas { |
控件開發(fā)最重要的就是控件的繪制了。控件的繪制可以通過添加PaintListener,在它的paintControl方法中進行。
addPaintListener(new PaintListener() { |
這里要注意的是從PaintEvent中獲取的x,y,height,width是需要重繪的區(qū)域,x,y是以控件的左上角為原點的坐標。在我們的 程序中,為了性能起見,我們先根據(jù)需要重繪的區(qū)域計算出需要重繪的行數(shù),只重繪相應的行,而不是將整個控件重繪。我們程序中用到的onPaint用于繪制 一行。
接下來,我們要讓我們的控件響應鍵盤上下鍵對列表項進行選擇。我們已對向上鍵的處理為例,首先當用戶按了向上鍵時,我們需要改變選擇,并且重繪舊的和新的選擇項。如果選擇項已經(jīng)到了列表的頂部,我們還需要同時滾動滾動條。
addListener(SWT.KeyDown, new Listener() { |
接下來,我們要讓我們的控件響應鼠標對列表項進行選擇。首先我們要計算出鼠標選中的行號,注意MouseEvent中的y值只是相對于控件左上角的坐標,我們需要加上滾動出了控件的部分。
addMouseListener(new MouseListener() { |
當我們的控件獲得焦點時,選中的列表項需要有虛框表示控件得到焦點。當獲得或失去焦點是,我們這里只需要簡單的通知選中的項重畫。
addFocusListener(new FocusListener() { |
我們在繪制每一個列表項時可以加入判斷當前控件是否得到焦點,如果控件得到了焦點,我們就在選中的項目上畫一個虛框。下面是我們繪制一個列表項的代碼,注意在代碼的最后繪制焦點的虛框。
void onPaint(GC gc, int row, int beginx, int beginy, boolean isSelected) { |
作為一個可操作的控件,TAB鍵的支持也是很重要的。由于我們的控件是從Canvas繼承過來的,不支持TAB鍵。下面的代碼使我們的控件有TAB鍵的支持:
addTraverseListener(new TraverseListener() { |
很多時候,我們需要有滾動條的支持。對于滾動條,我們只要在上面加上selectionListener,處理它的widgetSelected事件就可以。
bar = getVerticalBar(); |
下面是函數(shù)scrollVertical的代碼。一旦用戶對滾動條操作,我們就可以計算出要滾動的區(qū)域,然后調(diào)用scroll函數(shù)。對函數(shù)scroll函數(shù)的調(diào)用會導致相應區(qū)域的重繪。
void scrollVertical(ScrollBar scrollBar) { |
現(xiàn)在我們的程序已經(jīng)基本成形了,我們來進一步完善它。由于我們開發(fā)的控件是提供給程序員的,我們需要提供接口,讓外部知道控件中發(fā)生的事件。其中最 重要的是列表項的選中事件。我們需要提供接口讓程序員能夠添加事件監(jiān)控器(listener)來監(jiān)控發(fā)生的事件,并且一旦發(fā)生事件,我們需要通知監(jiān)控器。
首先,我們添加一個成員來保存添加的事件監(jiān)控器:
Vector selectionListeners = new Vector(); |
我們再增加一個函數(shù)addSelectionListener,讓程序員可以添加監(jiān)控器
public void addSelectionListener(SelectionListener listener) { |
在我們前面的代碼中,我們注意到每次選擇項改變,我們都會調(diào)用selectionChanged函數(shù)。下面是selectionChanged函數(shù) 代碼。這里,我們會生成一個SelectionEvent事件,并且逐個調(diào)用事件監(jiān)控器的widgetSelected方法。這樣別人就可以監(jiān)聽到我們的 事件了。
public void selectionChanged() { |
現(xiàn)在輔助功能(Accessibility)也日益成為軟件重要的部分,它是的殘疾人也能夠方便的使用我們的軟件。美國已經(jīng)立法,不符合 Accessibility規(guī)范的軟件不能夠在政府部門銷售。我們開發(fā)的控件也需要支持Accessibility.下面的代碼使我們的控件有 Accessibility支持。其中最重要的是getRole和getValue函數(shù)。我們的控件是從Canvas繼承,我們在getRole函數(shù)中返 回ACC.ROLE_LIST,這樣我們的控件才能讓屏幕閱讀軟件將我們的控件作為列表控件對待。
Accessible accessible = getAccessible(); accessible.addAccessibleControlListener(new AccessibleControlAdapter() { public void getRole(AccessibleControlEvent e) { int role = 0; int childID = e.childID; if (childID == ACC.CHILDID_SELF) { role = ACC.ROLE_LIST; } else if (childID >= 0 && childID < colors.size()) { role = ACC.ROLE_LISTITEM; } e.detail = role; } public void getValue(AccessibleControlEvent e){ int childID = e.childID; if (childID == ACC.CHILDID_SELF) { e.result = getText(); } else if (childID >= 0 && childID < colors.size()) { e.result = (String)colorNames.get(childID); } } public void getChildAtPoint(AccessibleControlEvent e) { Point testPoint = toControl(new Point(e.x, e.y)); int childID = ACC.CHILDID_NONE; childID = (testPoint.y - cy)/lineHeight; if (childID == ACC.CHILDID_NONE) { Rectangle location = getBounds(); location.height = location.height - getClientArea().height; if (location.contains(testPoint)) { childID = ACC.CHILDID_SELF; } } e.childID = childID; } public void getLocation(AccessibleControlEvent e) { Rectangle location = null; int childID = e.childID; if (childID == ACC.CHILDID_SELF) { location = getBounds(); } if (childID >= 0 && childID < colors.size()) { location = new Rectangle(cx,childID*lineHeight+cy,maxX,lineHeight); } if (location != null) { Point pt = toDisplay(new Point(location.x, location.y)); e.x = pt.x; e.y = pt.y; e.width = location.width; e.height = location.height; } } public void getChildCount(AccessibleControlEvent e) { e.detail = colors.size(); } public void getState(AccessibleControlEvent e) { int state = 0; int childID = e.childID; if (childID == ACC.CHILDID_SELF) { state = ACC.STATE_NORMAL; } else if (childID >= 0 && childID < colors.size()) { state = ACC.STATE_SELECTABLE; if (isFocusControl()) { state |= ACC.STATE_FOCUSABLE; } if (rowSel == childID) { state |= ACC.STATE_SELECTED; if (isFocusControl()) { state |= ACC.STATE_FOCUSED; } } } e.detail = state; } });最后,我們需要提供一些方法方便程序員使用我們的控件。
public void setSelection(int index) { |
我們開發(fā)的控件的使用也是非常簡單的。
CustomList customlist = new CustomList( parent, SWT.V_SCROLL | SWT.H_SCROLL ); |
以上我們介紹了如何開發(fā)一個簡單的自定義控件所需要涉及的技術。這里我們只以一個簡單的顏色控件為例,但是一旦我們掌握了方法,我們很容易就可以開發(fā)出各種不同的漂亮控件。
整個程序完整的代碼清參考:ColorList.java。
posted on 2008-03-19 18:01 gembin 閱讀(3132) 評論(1) 編輯 收藏 所屬分類: Eclipse RCP 、SWT