<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    gembin

    OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

    HBase, Hadoop, ZooKeeper, Cassandra

    Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

    There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

    About Me

     

    SWT 自定義控件

    現在基于Eclipse的應用越來越多,很多桌面應用都是用Eclipse開發的。Eclipse提供了一套SWT/JFACE 的控件庫,使得人們開發界面應用極大的方便。但是,SWT/JFACE的控件庫畢竟有限,在應用開發是我們不可避免地要自己開發一些自定義的控件。本文通 過開發一個顏色列表控件的實例介紹了Eclipse自定義控件開發中所要用到的技術。

    目標讀者必須熟悉Java開發,并且有一定的Eclipse開發經驗。

    在Eclipse網站上有一篇相關的文章"Creating Your Own Widgets using SWT",該文介紹了開發自己控件的很多基本概念、方法,并且通過實例進行了介紹,非常好。但是其所用的實例比較簡單,還有很多控件開發中所要涉及到的內 容,例如鍵盤、鼠標事件的處理,滾動條、焦點的處理等等沒有提及。本文通過開發一個自定義的顏色列表控件的實例,全面地介紹了自定義控件所涉及的技術。同 時,讀者也可以對該實例進行擴展,實現自己的列表控件。

    SWT中提供的標準列表控件非常簡單,只能提供字符串的選擇。我們經常需要提供一些圖形列表供用戶選擇,這就需要自己開發自定義的列表控件。顏色選擇列表是我們常用的一種圖形列表,我們就以此為例進行介紹。以下是我們將要開發的顏色列表。

    我們在開發自定義控件時主要考慮以下問題:

    1、 自定義控件的繪制:通常我們需要自己對控件的形狀或圖案進行繪制;

    2、 控件對鍵盤事件的響應:當焦點進入控件,用戶進行鍵盤操作,通過鍵盤對控件進行控制時,我們需要讓控件對用戶的操作進行響應。例如在列表中,用戶會通過上下箭頭改變列表的選擇項;

    3、 控件對鼠標事件的響應:當用戶用鼠標選中控件,進行操作時,控件必須作出相應的反應;

    4、 控件對焦點事件的響應:當界面焦點進入或移出控件,通常我們需要將控件繪制成得到或失去焦點的形狀。例如,當焦點進入列表時,一般被選中的列表項會有虛框表示選中。

    5、 響應TAB鍵:對于一個可操縱的控件,用戶可以用TAB鍵將焦點移入或移出。

    6、 響應滾動條事件:當控件有滾動條時,我們需要響應用戶對滾動條的操作,完成對控件的繪制工作。

    7、 提供事件監聽機制:程序員使用你的控件時通常需要監聽控件中發生的一些事件,這樣當事件發生時,他們能夠進行相應處理。

    8、 提供輔助功能(Accessibility):輔助功能是方便殘障人士使用時必須的,標準控件都會提供相應的支持,我們自定義的控件也不例外。

    9、 提供功能接口方便程序員訪問:通常為方便程序員使用時獲取控件中的信息或進行設置,我們需要提供一些接口。

    首先我們要開發的列表控件是一個基本控件,所以我們選擇Canvas作為我們開發的基類。


    		public class ColorList extends Canvas {
    Vector colors = new Vector(); // 用于保存我們顏色控件中的顏色值
    Vector colorNames = new Vector(); // 用于保存顏色控件中的顏色名字

    int rowSel = -1; // 用于保存當前選中的行號
    int oldRowSel = -1; // 用于保存上一次選中的行號

    int maxX, maxY; // 用于保存列表的寬度和高度
    int lineHeight; // 用于設置行高

    int cx = 0; // 滾動條滾動后,控件的圖形相對于控件可見區域左上角的x坐標
    int cy = 0; // 滾動條滾動后,控件的圖形相對于控件可見區域左上角的y坐標
    }

    控件開發最重要的就是控件的繪制了。控件的繪制可以通過添加PaintListener,在它的paintControl方法中進行。


    		addPaintListener(new PaintListener() {
    public void paintControl(PaintEvent e) {
    GC gc = e.gc;
    Point size = getSize();
    int beginx = e.x;
    int beginy = (e.y / lineHeight) * lineHeight;
    int beginLine = (e.y - cy) / lineHeight;
    int endLine = beginLine + e.height / lineHeight + 1;
    if (endLine > getItemCount())
    endLine = getItemCount();
    for (int i = beginLine; i < endLine; i++) {
    boolean selected = false;
    if (i == rowSel)
    selected = true;
    onPaint(gc, i, cx, beginy + (i - beginLine) * lineHeight,
    selected);
    }
    }
    });

    這里要注意的是從PaintEvent中獲取的x,y,height,width是需要重繪的區域,x,y是以控件的左上角為原點的坐標。在我們的 程序中,為了性能起見,我們先根據需要重繪的區域計算出需要重繪的行數,只重繪相應的行,而不是將整個控件重繪。我們程序中用到的onPaint用于繪制 一行。

    接下來,我們要讓我們的控件響應鍵盤上下鍵對列表項進行選擇。我們已對向上鍵的處理為例,首先當用戶按了向上鍵時,我們需要改變選擇,并且重繪舊的和新的選擇項。如果選擇項已經到了列表的頂部,我們還需要同時滾動滾動條。


    		addListener(SWT.KeyDown, new Listener() {
    public void handleEvent(Event event) {
    switch (event.keyCode) {
    case SWT.ARROW_UP: // 處理向上鍵
    if (rowSel != 0) {
    oldRowSel = rowSel;
    rowSel--;
    if (oldRowSel != rowSel) { //發送消息讓控件重繪
    ((Canvas) event.widget).redraw(cx, (rowSel + cy
    / lineHeight)
    * lineHeight, maxX, lineHeight*2, false);
    }
    if (rowSel < -cy / lineHeight) { //如果需要,滾動滾動條
    ScrollBar bar = ((Canvas) event.widget)
    .getVerticalBar();
    bar.setSelection(bar.getSelection() - lineHeight);
    scrollVertical(bar);
    }
    selectionChanged(); // 發送selectionChanged事件
    }
    break;
    case SWT.ARROW_DOWN: // down arror key

    break;
    }
    }
    });

    接下來,我們要讓我們的控件響應鼠標對列表項進行選擇。首先我們要計算出鼠標選中的行號,注意MouseEvent中的y值只是相對于控件左上角的坐標,我們需要加上滾動出了控件的部分。


    		addMouseListener(new MouseListener() {
    public void mouseDoubleClick(MouseEvent e) {
    }
    public void mouseDown(MouseEvent e) {
    int row = (e.y - cy) / lineHeight; //計算選中的行
    if (row >= 0) {
    oldRowSel = rowSel;
    rowSel = row;
    }
    if (oldRowSel != rowSel) { // 重畫舊的和新的選擇項
    ((Canvas) e.getSource()).redraw(cx, (e.y / lineHeight)
    * lineHeight, maxX, lineHeight, false);
    ((Canvas) e.getSource()).redraw(cx, (oldRowSel + cy
    / lineHeight)
    * lineHeight, maxX, lineHeight, false);
    }
    selectionChanged();
    }
    public void mouseUp(MouseEvent e) {
    }
    });

    當我們的控件獲得焦點時,選中的列表項需要有虛框表示控件得到焦點。當獲得或失去焦點是,我們這里只需要簡單的通知選中的項重畫。


    		addFocusListener(new FocusListener() {
    public void focusGained(FocusEvent e) {
    ((Canvas) e.getSource()).redraw(cx, rowSel * lineHeight, maxX,
    lineHeight, true);
    }
    public void focusLost(FocusEvent e) {
    ((Canvas) e.getSource()).redraw(cx, rowSel * lineHeight, maxX,
    lineHeight, true);
    }
    });

    我們在繪制每一個列表項時可以加入判斷當前控件是否得到焦點,如果控件得到了焦點,我們就在選中的項目上畫一個虛框。下面是我們繪制一個列表項的代碼,注意在代碼的最后繪制焦點的虛框。


    	void onPaint(GC gc, int row, int beginx, int beginy, boolean isSelected) {
    Color initColor = gc.getBackground();
    Color initForeColor = gc.getForeground();
    if (isSelected) {
    gc.setBackground(Display.getCurrent().getSystemColor(
    SWT.COLOR_LIST_SELECTION));
    gc.fillRectangle(beginx, beginy, maxX, lineHeight);
    gc.setForeground(Display.getCurrent().getSystemColor(
    SWT.COLOR_LIST_SELECTION_TEXT));
    } else {
    gc.setBackground(initColor);
    }
    gc.drawString((String) colorNames.get(row), beginx + 24, beginy);
    Color color = Display.getCurrent().getSystemColor(
    ((Integer) colors.get(row)).intValue());
    gc.setBackground(color);
    gc.fillRectangle(beginx + 2, beginy + 2, 20, lineHeight - 4);
    gc.setBackground(initColor);
    gc.setForeground(initForeColor);
    if (isFocusControl() && isSelected)
    gc.drawFocus(cx, beginy, maxX, lineHeight);
    }

    作為一個可操作的控件,TAB鍵的支持也是很重要的。由于我們的控件是從Canvas繼承過來的,不支持TAB鍵。下面的代碼使我們的控件有TAB鍵的支持:


    addTraverseListener(new TraverseListener() {
    public void keyTraversed(TraverseEvent e) {
    if (e.detail == SWT.TRAVERSE_TAB_NEXT
    || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
    e.doit = true;
    }
    };
    });

    很多時候,我們需要有滾動條的支持。對于滾動條,我們只要在上面加上selectionListener,處理它的widgetSelected事件就可以。


    bar = getVerticalBar();
    if (bar != null) {
    bar.addSelectionListener(new SelectionAdapter() {
    public void widgetSelected(SelectionEvent event) {
    scrollVertical((ScrollBar) event.widget);
    }
    });
    }

    下面是函數scrollVertical的代碼。一旦用戶對滾動條操作,我們就可以計算出要滾動的區域,然后調用scroll函數。對函數scroll函數的調用會導致相應區域的重繪。


    void scrollVertical(ScrollBar scrollBar) {
    Rectangle bounds = getClientArea();
    int y = -scrollBar.getSelection();
    if (y + maxY < bounds.height) {
    y = bounds.height - maxY;
    }
    if( y%lineHeight !=0 )
    y = y - y % lineHeight - lineHeight;
    scroll(cx, y, cx, cy, maxX, maxY, false);
    cy = y;
    }

    現在我們的程序已經基本成形了,我們來進一步完善它。由于我們開發的控件是提供給程序員的,我們需要提供接口,讓外部知道控件中發生的事件。其中最 重要的是列表項的選中事件。我們需要提供接口讓程序員能夠添加事件監控器(listener)來監控發生的事件,并且一旦發生事件,我們需要通知監控器。

    首先,我們添加一個成員來保存添加的事件監控器:


    Vector selectionListeners = new Vector();

    我們再增加一個函數addSelectionListener,讓程序員可以添加監控器


    public void addSelectionListener(SelectionListener listener) {
    selectionListeners.addElement(listener);
    }

    在我們前面的代碼中,我們注意到每次選擇項改變,我們都會調用selectionChanged函數。下面是selectionChanged函數 代碼。這里,我們會生成一個SelectionEvent事件,并且逐個調用事件監控器的widgetSelected方法。這樣別人就可以監聽到我們的 事件了。


    public void selectionChanged() {
    Event event = new Event();
    event.widget = this;
    SelectionEvent e = new SelectionEvent(event);
    for (int i = 0; i < selectionListeners.size(); i++) {
    SelectionListener listener = (SelectionListener) selectionListeners.elementAt(i);
    listener.widgetSelected(e);
    }
    }

    現在輔助功能(Accessibility)也日益成為軟件重要的部分,它是的殘疾人也能夠方便的使用我們的軟件。美國已經立法,不符合 Accessibility規范的軟件不能夠在政府部門銷售。我們開發的控件也需要支持Accessibility.下面的代碼使我們的控件有 Accessibility支持。其中最重要的是getRole和getValue函數。我們的控件是從Canvas繼承,我們在getRole函數中返 回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) {
    if (index >= getItemCount() || index < 0)
    return;
    oldRowSel = rowSel;
    rowSel = index;
    selectionChanged();
    }
    public int getSelectionIndex() {
    return rowSel;
    }
    public int getItemHeight() {
    return lineHeight;
    }
    public void setItemHeight(int height) {
    lineHeight = height;
    }
    public int getItemCount() {
    return colors.size();
    }
    public void add(int colorIndex, String colorName) {
    colorNames.add(colorName);
    colors.add(new Integer(colorIndex));
    }

    我們開發的控件的使用也是非常簡單的。


    CustomList customlist = new CustomList( parent, SWT.V_SCROLL | SWT.H_SCROLL );
    customlist.add(SWT.COLOR_BLACK,"BLACK");
    customlist.add(SWT.COLOR_BLUE,"BLUE");
    customlist.setSelection(1);
    customlist.setSize(400,400);
    customlist.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));

    以上我們介紹了如何開發一個簡單的自定義控件所需要涉及的技術。這里我們只以一個簡單的顏色控件為例,但是一旦我們掌握了方法,我們很容易就可以開發出各種不同的漂亮控件。

    整個程序完整的代碼清參考:ColorList.java

    posted on 2008-03-19 18:01 gembin 閱讀(3142) 評論(1)  編輯  收藏 所屬分類: Eclipse RCPSWT

    評論

    # re: SWT 自定義控件 2008-05-22 16:15 fy_kenny

    好文章  回復  更多評論   

    導航

    統計

    常用鏈接

    留言簿(6)

    隨筆分類(440)

    隨筆檔案(378)

    文章檔案(6)

    新聞檔案(1)

    相冊

    收藏夾(9)

    Adobe

    Android

    AS3

    Blog-Links

    Build

    Design Pattern

    Eclipse

    Favorite Links

    Flickr

    Game Dev

    HBase

    Identity Management

    IT resources

    JEE

    Language

    OpenID

    OSGi

    SOA

    Version Control

    最新隨筆

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    free counters
    主站蜘蛛池模板: 一本色道久久88—综合亚洲精品| 中文字幕永久免费| 亚洲偷自拍拍综合网| 久久精品国产免费一区| 亚洲欧洲国产经精品香蕉网| 国产成人青青热久免费精品| 两性色午夜免费视频| 色在线亚洲视频www| 国产午夜亚洲精品国产成人小说| 亚洲免费在线视频观看| 美女被爆羞羞网站在免费观看| 久久久亚洲精品无码| 日韩一品在线播放视频一品免费| 久久国产精品免费一区二区三区| 亚洲一区二区久久| 亚洲自偷自偷偷色无码中文| 日本一区二区三区免费高清| 在线免费播放一级毛片| 亚洲国产一区二区三区在线观看 | 在线成人爽a毛片免费软件| 含羞草国产亚洲精品岁国产精品| 久久香蕉国产线看观看亚洲片| 全免费A级毛片免费看网站| 无码A级毛片免费视频内谢| 免费的黄色的网站| 亚洲一区二区三区免费在线观看 | 免费a级毛片18以上观看精品| 24小时免费看片| 国产一级a毛一级a看免费人娇| 亚洲一卡2卡3卡4卡5卡6卡 | 亚洲日本乱码一区二区在线二产线 | 91免费在线视频| 深夜福利在线免费观看| 亚洲综合精品成人| 亚洲精品美女视频| 亚洲AV无码国产在丝袜线观看| 国产成人亚洲综合| 国产乱子伦片免费观看中字| 色妞WWW精品免费视频| 亚洲国产精品免费在线观看| 黄色视频在线免费观看|