程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> SWT和JFace,第3部分: 簡介

SWT和JFace,第3部分: 簡介

編輯:關於JAVA

在本系列的前兩期中(請參閱 參考資料),我介紹了 Eclipse、Eclipse Standard Widget Toolkit (SWT) 和 JFace GUI 工具包,以構造 Eclipse 和單獨的富 GUI(rich GUI)。還介紹了一些基本 GUI 控件和容器類型。然後展示了如何將這些控件組合到簡單的工作應用程序中。我詳細介紹了如何在一個菜單系統中提供這些應用程序。最後,通過創建一個使 GUI 開發變得更容易的方法的庫,我演示了如何遵循最佳實踐。

在這一期中,我將繼續查看 org.eclipse.swt.custom 和 org.eclipse.swt.widgets 包中的各種小部件。除非另外注明,否則我所討論的控件都位於 widgets 包中。

TableTree

在 第 2 部分 中,我介紹了 Tree 和 Table 控件。SWT 支持這些控件的一個混合版本,在 custom 包中,這些控件被稱為 TableTree。在 Eclipse V3.1 中,Tree 控件被增強為 TableTree 的一個功能替代,TableTree 則遭到反對。圖 1 展示了一個表格式(TableTree 仿真模式)的示例 Tree。正如您可以看到的,樹中的每一個項都被劃分到列中。“第 2 部分”中展示了如何創建這些表和樹,而創建一個表 Tree 實質上就是將這兩項任務組合在一起,在這裡我就不再重復它們。本文中包含的 Tree 示例將演示如何創建表 Tree。創建 TableTree 的代碼與創建 Tree 的代碼非常相似,因此,如果需要支持 Eclipse 的以前版本,那麼可以使用 TableTree 控件。

圖 1. 表樹的例子

本文的其余部分將展示如何使用許多新的 SWT 控件。我將在一個稱為 TabFolder1App 的單個應用程序的上下文中做這一介紹。

TabFolder(和 CTabFolder)

TabFolders 是一個創建使用有限數量空間的復雜 GUI 的簡便方法。一個選項卡文件夾(tab folder)被分成一個或多個選項卡(tab),其中每個選項卡都是它本身的一個完整 GUI。一次只顯示一個選項卡。在 custom 包中,CTabFolder 是 TabFolder 的增強版,它看起來更好一些,並且可以支持選項卡的關閉。

必須將 TabFolders 和 CTabFolders 定義為以下兩個相互排斥的樣式之一:

TOP —— 將選項卡放置在頂部。

BOTTOM —— 將選項卡放置在底部。

CTabFolder 支持其他一些可選樣式:

FLAT —— 為文件夾提供一個扁平的外觀。

BORDER —— 在控件的周圍顯示邊界。

CLOSE —— 允許選項卡關閉(顯示一個 Close 按鈕)。

與包含一些項的 Trees 和 Tables 類似,TabFolders 也包含一些定義選項卡的 TabItems(或者 CTabItems)。TabFolders 還包含多個控件(通常是 Composites),每個控件都定義了選項卡的一個內容。TabItem.setControl 方法將該控件與相關的選項卡連接起來。

圖 2 展示了一個示例 TabFolder,而圖 3 展示了一個使用 CTabFolder 的類似 GUI。注意,選定的 Canvas 選項卡在 CTabFolder 上有一個 Close (X) 按鈕。

圖 2. 帶有 4 個選項卡的 TabFolder

圖 3. 帶有 4 個選項卡的 CTabFolder

與 第 2 部分 中介紹的方法一致,我將使用 protected 服務方法(位於超類 BasicApplication 中)來創建控件。清單 1 詳細介紹了這些方法中的第一種方法,並展示了如何創建 TabFolders;也存在用於創建 CTabFolders 的類似代碼。

清單 1. 用於創建 TabFolder 和 TabItem 的方法

protected TabFolder createTabFolder(Composite parent, int style) {
  return new TabFolder(parent, style);
}
protected TabItem createTabItem(TabFolder parent, int style,
           String text, Image icon, Control ctl) {
  TabItem ti = new TabItem(parent, style);
  if (text != null) {
    ti.setText(text);
  }
  if (icon != null) {
    ti.setImage(icon);
  }
  if (ctl != null) {
    ti.setControl(ctl);
  }
  return ti;
}
protected TabItem createTabItem(TabFolder parent,
           String text, Image icon, Control ctl) {
  return createTabItem(parent, SWT.NONE, text, icon, ctl);
}

Canvas

Canvas 是最基本的控件類型之一,可以用它來創建定制控件或繪圖。圖 2 和 圖 3 展示了使用 Canvas 來繪制由重疊的矩形和橢圓形組成的圖片的一個例子。在這幅繪畫中,一些圖片被填充,而其他一些則沒有被填充。顏色、大小和位置的分配是隨意的。

清單 2 展示了用於創建 Canvas 的代碼。要實際地在 Canvas 上進行繪圖,必須向該 Canvas 添加一個 PaintListener。每當 Canvas 需要重新繪制其客戶機區域的任何部分時,都需要調用其 paintControl 方法。有兩種繪制風格:

直接繪制 —— 很簡單,但內容在整個重繪期間是不穩定的。

在進行繪制之前構建一個模型,然後再根據此模型進行重新繪制 —— 比較復雜,但很穩定。這通常是首選方法。

清單 2. 用於創建 Canvas 的方法

protected Canvas createCanvas(Composite parent, int style,
                PaintListener pl) {
   Canvas c = new Canvas(parent, style);
   if (pl != null) {
     c.addPaintListener(pl);
   }
   return c;
}
protected Canvas createCanvas(Composite parent, PaintListener pl) {
   return createCanvas(parent, SWT.NONE, pl);
}

作為繪制風格 2 的一個例子,可以考慮一下清單 3 中定義的簡單模型:

清單 3. PaintItems 的層次結構

abstract protected class PaintItem {
   public Color color;
   public void paint(GC gc) {
     gc.setForeground(color);
     gc.setBackground(color);
   }
}
abstract protected class BaseRectItem extends PaintItem {
   public boolean fill;
   public Rectangle extent;
}
protected class ElipseItem extends BaseRectItem {
   public void paint(GC gc) {
     super.paint(gc);
     if (fill) {
       gc.fillOval(extent.x, extent.y,
             extent.width, extent.height);
     }
     else {
       gc.drawOval(extent.x, extent.y,
             extent.width, extent.height);
     }
   }
}
protected class RectangleItem extends BaseRectItem {
   public void paint(GC gc) {
     super.paint(gc);
     if (fill) {
       gc.fillRectangle(extent.x, extent.y,
               extent.width, extent.height);
     }
     else {
       gc.drawRectangle(extent.x, extent.y,
               extent.width, extent.height);
     }
   }
}

這些繪制項都由 清單 4 中顯示的 PaintListener 繪制。paintControl 方法是隨在其上進行繪制的圖形上下文(org.eclipse.swt.graphics 包中的 GC)一起提供的。您可以使用 GC 繪制文本和許多形狀。此代碼將重用通過 Display 類可用的標准系統顏色。由 Canvas 決定是否使用某種背景色填充其區域。gcObjects 集合包含所有需要繪制的 PaintItem 實例。數組 colorIds 是一個到選定的系統顏色的映射。

清單 4. 用於創建 TabFolder 和 TabItem 的方法

… new PaintListener() {
   public void paintControl(PaintEvent e) {
     GC gc = e.gc;
     gc.setBackground(canvas.getDisplay().
       getSystemColor(colorIds[0]));
     Point cext = canvas.getSize();
     gc.fillRectangle(0, 0, cext.x, cext.y);
     for (Iterator i = gcObjects.iterator();
       i.hasNext();) {
       PaintItem pi = (PaintItem)i.next();
       pi.paint(gc);
     }
  }
}…
protected static int[] colorIds = {
   SWT.COLOR_WHITE, SWT.COLOR_BLUE, SWT.COLOR_CYAN,
   SWT.COLOR_GRAY, SWT.COLOR_GREEN, SWT.COLOR_MAGENTA,
   SWT.COLOR_RED, SWT.COLOR_YELLOW, SWT.COLOR_BLACK
};

清單 5 中顯示了一些代碼,這些代碼先清除繪畫,然後創建由一組矩形和橢圓組成的繪畫。通過 GUI 上的按鈕可以激活此代碼。

清單 5. 用於處理繪制事件的方法

public void doClear() {
   gcObjects.clear();
   canvas.redraw();
}
public void doDraw() {
   gcObjects.clear();
   Display display = drawButton.getDisplay();
   // create a bunch of objects
   for (int i = 0; i < 50; i++) {
     if (i % 2 == 0) {
       RectangleItem ri = new RectangleItem();
       ri.extent = new Rectangle(nextInt(500), nextInt(250),
                    nextInt(500), nextInt(250));
       ri.color = display.
         getSystemColor(colorIds[nextInt(colorIds.length)]);
       ri.fill = i % 3 == 0;
       gcObjects.add(ri);
     }
     else {
       ElipseItem ei = new ElipseItem();
       ei.extent = new Rectangle(nextInt(500), nextInt(250),
                    nextInt(500), nextInt(250));
       ei.color = display.
         getSystemColor(colorIds[nextInt(colorIds.length)]);
       ei.fill = i % 5 == 0;
       gcObjects.add(ei);
     }
   }
   canvas.redraw();
}

Spinner、Slider、Scale 和 ProgressBar

SWT 支持幾種輸入離散值的方法。Scale 允許在(通常很小的)整數范圍內挑選一個值。Slider 允許使用類似滾動條的方法在(可能很大的)整數范圍內挑選一個值。Spinner 允許挑選(通過向前/撤退按鈕)或鍵入一個(可能為小數的)數字。注意,Spinner 是 Eclipse V3.1 中的一個新特性。ProgressBar 類似於一個只輸出的 Slider,因為可以用它來展示增量活動(進度)。

通常,這些控件允許您提供最小值、最大值和初始值。除了 ProgressBars 之外,這些控件還支持增量值和頁面增量值,Sliders 還支持 thumb 寬度。圖 4 展示了一個 GUI,它在控件組內包含一個 Slider、一個 Spinner 和一個 Scale,在這些控件的下方是一個 ProgressBar。緊貼在進度條上的是一個(居中的)Label,它展示了進度條的值。

圖 4. 控件的例子

必須將所有這些控件定義為以下兩種相互排斥的樣式之一:

HORIZONTAL —— 水平地布置控件。

VERTICAL —— 垂直地布置控件。

Spinners 支持其他一些可選樣式:

WRAP —— 從高值向低值換行排列。

READ_ONLY —— 不允許鍵入輸入值。

ProgressBars 支持其他一些可選樣式:

SMOOTH —— 更新不是在截然不同的步驟中進行的。

INDETERMINATE —— 沒有預先確定步驟數的范圍;進度條只是在時間上重復。

要創建這些控件,可以使用清單 6-9 中所示的代碼。正如在 第 2 部分 中所描述的,將通過 registerCallback 方法,使用 Java 反射將 SelectionListeners 添加到這些控件中。每當控件的值發生更改時,都會調用此偵聽器。

清單 6. 用於創建 Slider 的方法

protected Slider createSlider(Composite parent, int style,
                int min, int current, int max,
                int inc, int pageinc, int thumb,
                String callback) {
   Slider s = new Slider(parent, style);
   if (min >= 0) {
     s.setMinimum(min);
   }
   if (max >= 0) {
     s.setMaximum(max);
   }
   if (current >= 0) {
     s.setSelection(current);
   }
   if (inc >= 1) {
     s.setIncrement(inc);
   }
   if (pageinc >= 1) {
     s.setPageIncrement(pageinc);
   }
   if (thumb >= 1) {
     s.setThumb(thumb);
   }
   if (callback != null) {
     registerCallback(s, this, callback);
   }
   return s;
}
protected Slider createVSlider(Composite parent,
                int min, int current, int max,
                int inc, int pageinc, int thumb,
                String callback) {
   return createSlider(parent, SWT.VERTICAL, min, current, max,
             inc, pageinc, thumb, callback);
}
protected Slider createHSlider(Composite parent,
                int min, int current, int max,
                int inc, int pageinc, int thumb,
                String callback) {
   return createSlider(parent, SWT.HORIZONTAL, min, current, max,
             inc, pageinc, thumb, callback);
}

清單 7. 用於創建 Spinner 的方法

protected Spinner createSpinner(Composite parent, int style,
                 int min, int current, int max,
                 int inc, int pageinc, String callback) {
   Spinner s = new Spinner(parent, style);
   if (min >= 0) {
     s.setMinimum(min);
   }
   if (max >= 0) {
     s.setMaximum(max);
   }
   if (current >= 0) {
     s.setSelection(current);
   }
   if (inc >= 1) {
     s.setIncrement(inc);
   }
   if (pageinc >= 1) {
     s.setPageIncrement(pageinc);
   }
   if (callback != null) {
     registerCallback(s, this, callback);
   }
   return s;
}

清單 8. 用於創建 Scale 的方法

protected Scale createScale(Composite parent, int style,
               int min, int current, int max,
               int inc, int pageinc) {
   Scale s = new Scale(parent, style);
   if (min >= 0) {
     s.setMinimum(min);
   }
   if (max >= 0) {
     s.setMaximum(max);
   }
   if (current >= 0) {
     s.setSelection(current);
   }
   if (inc >= 1) {
     s.setIncrement(inc);
   }
   if (pageinc >= 1) {
     s.setPageIncrement(pageinc);
   }
   return s;
}
protected Scale createVScale(Composite parent,
               int min, int current, int max,
               int inc, int pageinc) {
   return createScale(parent, SWT.VERTICAL, min, current, max,
            inc, pageinc);
}
protected Scale createHScale(Composite parent,
               int min, int current, int max,
               int inc, int pageinc) {
   return createScale(parent, SWT.HORIZONTAL, min, current, max,
            inc, pageinc);
}

清單 9. 用於創建 ProgressBar 的方法

protected ProgressBar createProgressBar(Composite parent, int style,
                     int min, int current, int max) {
   ProgressBar pb = new ProgressBar(parent, style);
   if (min >= 0) {
     pb.setMinimum(min);
   }
   if (max >= 0) {
     pb.setMaximum(max);
   }
   if (current >= 0) {
     pb.setSelection(current);
   }
   return pb;
}
protected ProgressBar createVProgressBar(Composite parent,
                     int min, int current, int max) {
   return createProgressBar(parent, SWT.VERTICAL, min, current, max);
}
protected ProgressBar createHProgressBar(Composite parent,
                     int min, int current, int max) {
   return createProgressBar(parent, SWT.HORIZONTAL, min, current, max);
}

您可以查詢或設置這些控件的當前值。考慮一下清單 10 中定義的線程,該線程將更新 圖 4 中的標簽、進度條和滑塊。此線程在選中“Automatic Update”按鈕(即代碼中的 modeButton)時啟動。

清單 10. 用於更新控件的線程

protected class BarUpdater extends Thread {
   protected int delay;
   protected Display display;
   public BarUpdater(Display display) {
     this.display = display;
   }
   public void run() {
     try {
       while (true) {
         try {
           if (!display.isDisposed()) {
             display.syncExec(new Runnable() {
                 public void run() {
                   if (!modeButton.isDisposed() &&
                     !scale.isDisposed()) {
                     delay = modeButton.getSelection()
                        ? scale.getSelection() : -1;
                   }
                 }
             });
             if (delay >= 0) {
               Thread.sleep(delay);
               if (!display.isDisposed()) {
                 display.syncExec(new Runnable() {
                   public void run() {
                     if (!bar.isDisposed()) {
                       int v = bar.getSelection() + 1;
                       if (v > bar.getMaximum()) {
                         v = bar.getMinimum();
                       }
                           bar.setSelection(v);
                       if (!slider.isDisposed()) {
                         slider.setSelection(v);
                       }
                       if (!valueLabel.isDisposed()) {
                         valueLabel.setText(
                            Integer.toString(v));
                       }
                     }
                   }
                 });
               }
             }
           }
           Thread.sleep(100);
         }
         catch (InterruptedException ie) {
         }
       }
     }
     catch (Exception e) {
       e.printStackTrace();
     }
   }
}

注意,此代碼小心地進行檢查,看各種控件在使用之前是否已經就緒。在異步 GUI 操作中,這很關鍵。還會注意到,所有 GUI 訪問都是在一個 syncExec(或其同類 asyncExec)方法中進行的。每當在與創建 GUI 所在的線程不同的線程上訪問 GUI 時,都需要這樣做。

StyledText

正如在 第 1 部分 中描述的那樣,SWT 通過 Text 控件支持純文本的輸入和顯示。對於更高級的文本表示形式,需要定義字體和顏色,因此可以使用 custom 包中的 StyledText 控件。StyledText 是可由許多 Eclipse 編輯器使用的控件。請考慮一下圖 5 中所示的樣式文本的示例。該文本包含不同的顏色和字體修飾,比如下劃線、刪除線、粗體和斜體。注意,刪除線和下劃線只在 Eclipse V3.1 上受到支持。

圖 5. StyledText 的例子

必須將 StyledText 定義為以下兩種相互排斥的樣式之一:

MULTI —— 顯示多個行。

SINGLE —— 顯示單個行。

StyledText 支持其他一些可選樣式:

WRAP —— 從控件的右邊換行。

READ_ONLY —— 不允許鍵入輸入值。

清單 11 顯示了用於創建 StyledText 的代碼。清單 12 使用簡單的類似 XML 的語言展示了它的用法,以定義具有這些屬性的文本的范圍。

清單 11. 用於創建 StyledText 的方法

protected StyledText createStyledText(Composite parent, int style) {
   return new StyledText(parent, style);
}

清單 12. StyledText 的例子

styledText = createStyledText(body, SWT.MULTI | SWT.WRAP |
                                    SWT.FULL_SELECTION);
styledText.addMouseListener(new MouseAdapter() {
    public void mouseDown(MouseEvent e) {
        if (e.button == 3) {
            processPopup();
        }
    }});
TextContent tc = new TextContent(body.getDisplay());
tc.setContent(dummyContent);
styledText.setText(tc.toPlainText());
styledText.setStyleRanges(tc.getStyleRanges());
 :
protected static final String dummyContent =
"Just plain text!\n" +
"Now is the time for <b>all</b> good men " +
"to come to the aid of their country\n" +
"<red><i>To <b>be</b></i> or <i>not to <b>be</b></i>?</red> " +
"<blue><u>That</u> is the <so>question</so></blue>\n" +
"That's all folks!\n";

StyledText 的例子使用了一個 helper 類 TextContent,以確定具有特殊屬性的文本的范圍。這個類包含在示例代碼中,支持對文檔進行分析並獲得其純文本內容,以及查找各種范圍,其中的純文本內容具有不同屬性或屬性組合。它創建了表示這些范圍的 SWT 類 StyleRange 的一個數組。StyleRange 有一些字段,這些字段描述了這樣的范圍,該范圍包括前景色和背景色,以及要應用的起始偏移量、長度和屬性,比如字體樣式(粗體或斜體)、刪除線和下劃線。

StyledText 所具有的功能比這裡展示的還要多。例如,在將文本輸入到文檔中時,文本沒有任何屬性,即使是在具有屬性的點輸入它也是如此。您可以使用 StyledText 的特性來改正這一行為。

PopupList

有時,當您希望能夠將一個選擇列表顯示為一個彈出式菜單而不用創建一個彈出式菜單時,可以使用 PopupList 控件做到這一點。圖 6 顯示了如何使用此控件將選擇、剪切、復制和粘貼功能添加到 圖 5 的文本編輯器中。

圖 6. PopupList 的例子

清單 13 顯示了 processPopup 方法的內容。注意,安置彈出式菜單的代碼與它所涉及的控件(即樣式文本)有關。

清單 13. PopupList 的例子

PopupList popup = createPopupList(shell, 50, popupItems);
popup.select(popupItems[0]);
Point p = styledText.getLocation();
p = shell.getDisplay().map(styledText, null, p.x, p.y);
String choice = popup.open(new Rectangle(
           p.x + 100, p.y - 100, 100, 200));
if (choice != null) {
   if   (popupItems[0].equals(choice)) {
     styledText.selectAll();
   }
   else if (popupItems[1].equals(choice)) {
     styledText.cut();
   }
   else if (popupItems[2].equals(choice)) {
     styledText.copy();
   }
   else if (popupItems[3].equals(choice)) {
     styledText.paste();
   }
}
:
protected static final String[] popupItems = {
   "Select All", "Cut", "Copy", "Paste"
};

StackLayout

在前兩篇文章中,我討論了隨 SWT 一起提供的幾個布局管理器,其中包括 FillLayout、GridLayout 和 FormLayout。custom 包提供了 StackLayout,可以用它在一次只顯示一個 GUI 的 TabFolder(有些類似於沒有選項卡的 TabFolder)的頂部放置多個 GUI。考慮一下 圖 7 和 圖 8,它們顯示了一個顯示帶編號標簽的堆棧布局的兩種狀態。通過“>>”按鈕可以讓堆棧前進,而通過“<<”按鈕則可以讓堆棧後退。圖 8 顯示了按下“>>”按鈕 4 次後的布局。

圖 7. StackLayout 的例子 1

圖 8. StackLayout 的例子 2

此堆棧由清單 14 中的代碼創建。

清單 14. StackLayout 的例子

StackLayout stackLayout = new StackLayout();
Composite clabels = createComposite(body, SWT.BORDER,
                   stackLayout);
Label[] labels = new Label[5];
:
for (int i = 0; i < labels.length; i++) {
   Label xlabel = new Label(clabels, SWT.CENTER);
   xlabel.setText("Stack " + i);
   labels[i] = xlabel;
}
stackLayout.topControl = labels[0];
:
protected Composite createComposite(Composite parent,
                   int style,
                   Layout layout) {
   Composite c = new Composite(parent, style);
   if (layout != null) {
     c.setLayout(layout);
   }
   return c;
}
protected Composite createComposite(Composite parent,
                   Layout layout) {
   return createComposite(parent, SWT.NONE, layout);
}

清單 15 顯示了通過“>>”按鈕到達下一個堆棧的代碼。對於“<<”按鈕,代碼與此類似。

清單 15. 前進到下一個堆棧的代碼

protected int currentLabel;
public void doNext() {
   ++currentLabel;
   if (currentLabel >= labels.length) {
     currentLabel = 0;
   }
   stackLayout.topControl = labels[currentLabel];
   clabels.layout();
}

結束語

在 SWT 和 JFace 系列的第三期中,我介紹了更多的 SWT 控件,比如用於創建表樹的 Tree;用於繪圖的 Canvas;用於輸入數字值的 Slider、Scale 和 Spinner;用於顯示進度的 ProgressBar;用於輸入具有某些屬性的文本的 StyledText;以及用於簡單動態菜單的 PopupList。我還展示了如何使用 StackLayout 創建時間合理的重疊 GUI。本系列的下一期將展示如何使用更多的 SWT 控件。

來源:http://www.ibm.com/developerworks/cn/opensource/os-jface3/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved