程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 深入學習JavaFX腳本語言(面向Swing程序員) ---(上)

深入學習JavaFX腳本語言(面向Swing程序員) ---(上)

編輯:關於JAVA

內容

比較聲明式語法與過程式語法

為程序增加動態行為

學習更多的JavaFX GUI組件

關於譯者

比較聲明式語法和過程式語法

正像我們在前一節所看到的,JavaFX語言提供了一種聲明式語法來表達結構和用戶接口組件的內容。為了幫助你理解,讓我們以純過程的方式重寫上面的程序,就像我們在編寫Swing程序時經常做的那樣:

var win = new Frame();
win.title = "Hello World JavaFX";
win.width = 200;
var label = new Label();
label.text = "Hello World";
win.content = label;
win.visible = true;

上面的源代碼同樣也是一個有效的JavaFX程序,它和前面的代碼具有相同的效果。

下面列舉出在聲明式方法和過程式方法中實際發生了什麼:

調用Frame類構造方法建立新的Frame。

給Frame的title、width、visible和content屬性賦值。

在賦值content屬性的過程中,調用了Label類的構造方法建立一個新的Label,並且為它的text屬性賦值。

盡管上面的代碼是一個非常簡單的示例,但根據第一個示例和上例的比較不難看出,采用聲明式語法編寫程序會使代碼更加清楚易懂。

因此,聲明式編程使用簡單的表達方式建立應用程序。在上面的第一個示例中,表達式的根(root)往往是一個對象分配表達式(構造器),它生成了組成程序的對象圖表(object graph)。

增加動態行為

目前,我們編寫的“Hello World”程序並沒有動態行為。為了使用JavaFX建立一個具有動態行為的圖形用戶接口組件,你可以建立這樣的圖形用戶接口:它的屬性依賴於其它對象的屬性值。而這些其它的對象將成為應用狀態的表象(representation),即它們代表了應用的狀態。由於這個GUI組件的屬性依賴於其它對象的屬性,因此此GUI組件將在你修改其它對象時自動地“反射”變化。在這裡,這個GUI組件通常被稱為視圖(View),而其它對象被稱為模型(Model)。下面是“Hello World”程序的模型/視圖版本。

class HelloWorldModel {
      attribute saying: String;
    }
    var model = HelloWorldModel {
      saying: "Hello World"
    };
    var win = Frame {
      title: "Hello World JavaFX"
      width: 200
      
      content: Label {
        text: bind model.saying
      }
      visible: true
    };

此程序運行結果如下圖:

如果model對象的saying屬性被修改為:

model.saying = "Goodbye Cruel World!";

那麼視圖將自動隨之改變,如下圖:

這裡值得注意的是:在上面的示例中,通過將JavaFX bind操作符應用於model的saying屬性,從而實現了對label的text屬性的初始化。在這裡,bind操作符標識了增量式(incremental)更新。這意味著無論model.saying的值何時改變,label的text屬性都將被更新為相同值。

在例如Buttons、CheckBoxes、TextFields這些輸入部件中,處於模型屬性和GUI組件屬性之間的關聯可以是雙向的。

請考慮以下示例:

class HelloWorldModel {
      attribute saying: String;
    }
    var model = HelloWorldModel {
      saying: "Hello World"
    };
    var win = Frame {
      title: bind "{model.saying} JavaFX"
      width: 200
      content: TextField {
        value: bind model.saying
      }
      visible: true
    };

此程序運行結果如下圖:

如果你在TextField中輸入其它的文字,並按下Enter,那麼窗體的標題將相應地改變:

在本例中,TextField的value屬性被更新為用戶輸入的文字(通過TextField類實現)。而與此同時,model的saying屬性也被更新為相同值。因為賦值給窗體的title屬性的表達式依賴於model的saying屬性,因此model的saying屬性的變化導致了表達式被重新求值、窗體的title屬性被更新。

注意:你能夠將任意組件的屬性bind到一個增量式求值表達式。這種表達式將使用條件邏輯、迭代器、選擇等產生任意復雜度的動態內容,而動態內容的表達式仍然保有其可聲明性。

學習更多的JavaFX GUI組件

本節將討論在JavaFX語言中可用的多種不同GUI組件,並給出展示其用途的示例程序,通過比較JavaFX組件與Swing GUI組件的方式討論它們的差異。

Border(邊框)和Layout Manager(布局管理器)

Menu(菜單)

Label(標簽)

Group Panel(分組面板),Simple Label(簡單標簽)和TextField(文本欄)

Button(按鈕)

ListBox(列表框)

SplitPane(分割窗體)

RadioButton(單選按鈕)、RadioButtonMenuItem(單選按鈕菜單項)、ToggleButton(開關按鈕)和ButtonGroup(按鈕分組)

ComboBox(下拉選擇框)

Tree(樹形)

Table(表格)

TextComponent(文本組件)

Spinner(微調控制器)和Slider(滑動條)

Border(邊框)和Layout Manager(布局管理器)

在JavaFX語言中,使用Border和Layout Manager也采用聲明的方式。每個Swing/AWT Layout Manager都被封裝在一個JavaFX類中,這個類使用指定的Layout Manager來實例化JPanel。被添加到JPanel上的組件被聲明為這個JavaFX類的屬性。每個Swing的Border類型也同樣被封裝在一個JavaFX類中,這個類具有與Swing Border配置選項對應的屬性。這裡提供了一個使用EmptyBorder和GridPanel的簡單示例。正如你所期待的那樣,JavaFX的EmptyBorder對應著javax.swing.border.EmptyBorder,而GridPanel則對應著java.awt.GridLayout。

class ButtonClickModel {
      attribute numClicks: Number;
    }
    var model = new ButtonClickModel();
    var win = Frame {
      width: 200
      content: GridPanel {
        border: EmptyBorder {
          top: 30
          left: 30
          bottom: 30
          right: 30
        }
        rows: 2
        columns: 1
        vgap: 10
        cells:
        [Button {
           text: "I'm a button!"
           mnemonic: I
           action: operation() {
             model.numClicks++;
          }
        },
        Label {
          text: bind "Number of button clicks: {model.numClicks}"
        }]
      }
      visible: true
    };

程序運行結果如下圖:

在點擊按鈕多次後,將出現如下效果:

需要注意的:對Button的action和mnemonic(助記碼)屬性的解釋將在下面給出..

在本例中,GridPanel被配置為單列、雙行、並在行間放置10個像素的垂直間隔,而這些工作僅僅通過為columns、rows和vgap屬性賦值來完成。如果你希望在列間建立一個間隔的話,GridPanel還提供hgap屬性。與此同時,一個30像素寬的空白邊框被設置在GridPanel的四邊。

通過把button和label賦值到cells屬性,我們可以在GridPanel上添加按鈕和標簽。GridPanel通過從其基類JPanel中增加或者刪除組件的方式來對在cells屬性上的插入或者刪除作出回應。

JavaFX支持的其它Layout Manager也采用同樣的方式。下面給出這些Layout Manager在JavaFX、Swing中的對應表格:

JavaFX部件 Layout Manager GridPanel GridLayout GridBagPanel GridBagLayout FlowPanel FlowLayout BorderPanel BorderLayout Box BoxLayout StackPanel Romain Guy's StackLayout CardPanel CardLayout GroupPanel org.jdesktop.layout.GroupLayout

下面是JavaFX Border類和其相應的Swing Border類的對應表格:

JavaFX Border Swing Border EmptyBorder EmptyBorder LineBorder LineBorder BevelBorder BevelBorder SoftBevelBorder SoftBevelBorder MatteBorder MatteBorder TitledBorder TitledBorder Menu(菜單)

讓我們在上一個示例的基礎上添加一個簡單的菜單條。新代碼如下:

import java.lang.System;
    class ButtonClickModel {
      attribute numClicks: Number;
    }
    var model = new ButtonClickModel();
    Frame {
      width: 200
      menubar: MenuBar {
         menus: Menu {
           text: "File"
           mnemonic: F
           items: MenuItem {
             text: "Exit"
             mnemonic: X
             accelerator: {
               modifier: ALT
               keyStroke: F4
             }
             action: operation() {
               System.exit(0);
             }
           }
         }
      }
      content: GridPanel {
        border: EmptyBorder {
          top: 30
          left: 30
          bottom: 30
          right: 30
        }
        rows: 2
        columns: 1
        vgap: 10
        cells:
        [Button {
           text: "I'm a button!"
           mnemonic: I
           action: operation() {
             model.numClicks++;
          }
        },
        Label {
          text: bind "Number of button clicks: {model.numClicks}"
        }]
      }
      visible: true
    }

在程序執行後,按下ALT+F組合鍵將出現如下情形:

正如你所見,我們通過將一個MenuBar類賦值到窗口的menubar屬性建立了一個菜單條。你可以通過增加menubar的menus來為menubar增加menu。在本例中,我們只添加了一個menu,但任何能夠返回Menu對象列表的表達式都能夠在這裡使用。

為了定義菜單,需要賦值menu的text、mnemonic、items的屬性值。

正如你所認為的那樣,text屬性的類型是String。 mnemonic屬性卻是KeyStroke類型的。它的值F是KeyStroke類的一個枚舉值。在JavaFX的屬性初始化程序上下文中,該屬性的靜態類(和在Java類中的靜態字段相似)的枚舉值能夠在沒有類型名限制的情況下被訪問(而在別處,你不得不把F寫為F:KeyStroke)。

這裡唯一的菜單項是一個MenuItem類實例,它具有值為"Exit"的text屬性和值為X的mnemonic屬性。而它的accelerator屬性也被賦值了。注意:在聲明中的類型名Accelerator被省略了。這在JavaFX中是允許的。如果類型名沒有提供,該屬性的靜態類型將被使用,在本例中accelerator屬性的靜態類型是Accelerator。另外,accelerator的modifier和keyStroke屬性都使用枚舉值進行了初始化。

最後,MenuItem的action屬性是function類型的(即指它的值為function,而非對象)。在本例中,action屬性是一個內聯(inline)的operation,它調用了一些Java代碼。

Label(標簽)

JavaFX的Label class支持HTML內容。通過使用Label,你可以利用HTML和CSS建立風格化文本(styled text)和圖片,它非常類似編寫典型的Web應用。另外,通過使用JavaFX內嵌表達式,你能夠在Swing應用中建立動態HTML內容,這就像在編寫Web頁面時使用JSTL或者Velocity等工具一樣容易。

請閱讀下面的購物卡示例:

class Item {
      attribute id: String;
      attribute productId: String;
      attribute description: String;
      attribute inStock: Boolean;
      attribute quantity: Number;
      attribute listPrice: Number;
      attribute totalCost: Number;
    }
    attribute Item.totalCost = bind quantity*listPrice;
    class Cart {
      attribute items: Item*;
      attribute subTotal: Number;
    }
    operation sumItems(itemList:Item*) {
      var result = 0.00;
      for (item in itemList) {
        result += item.totalCost;
      }
      return result;
    }
    attribute Cart.subTotal = bind sumItems(items);
    var cart = Cart {
      items:
      [Item {
        id: "UGLY"
        productId: "D100"
        description: "BullDog"
        inStock: true
        quantity: 1
        listPrice: 97.50
      },
      Item {
        id: "BITES"
        productId: "D101"
        description: "Pit Bull"
        inStock: true
        quantity: 1
        listPrice: 127.50
      }]
    };
    Frame {
       content: Label {
        text: bind
          "<html>
            <h2 align='center'>Shopping Cart</h2>
            <table align='center' border='0' bgcolor='#008800' cellspacing='2' cellpadding='5'>
             <tr bgcolor='#cccccc'>
               <td><b>Item ID</b></td>
               <td><b>Product ID</b></td>
               <td><b>Description</b></td>
               <td><b>In Stock?</b></td>
               <td><b>Quantity</b></td>
               <td><b>List Price</b></td>
               <td><b>Total Cost</b></td>
               <td></td>
              </tr>
              {
               if (sizeof cart.items == 0)
               then "<tr bgcolor='#FFFF88'><td colspan='8'><b>Your cart is empty.</b></td></tr>"
               else foreach (item in cart.items)
                 "<tr bgcolor='#FFFF88'>
                 <td>{item.id}</td>
                 <td>{item.productId}</td>
                 <td>{item.description}</td>
                 <td>{if item.inStock then "Yes" else "No"}</td>
                 <td>{item.quantity}</td>
                 <td align='right'>{item.listPrice}</td>
                 <td align='right'>{item.totalCost}</td>
                 <td></td>
                 </tr>"
              }
              <tr bgcolor='#FFFF88'>
                <td colspan='7' align='right'>
                  <b>Sub Total: ${cart.subTotal}</b>
                </td>
                <td></td>
              </tr>
             </table>
           </html>"
        }
        visible: true
    }

程序執行結果如下圖:

如果你通過編程刪去購物項(cart items):

delete cart.items;

程序將顯示如下結果:

在上面的示例中,嵌入的JavaFX表達式(在代碼中顯示為粗體)動態地建立HTML表格行和單元格的內容。當表達式所依賴的對象發生改變時,標簽的HTML內容也隨之動態地更新。

上面的示例非常有趣,因為它演示了如何使用表達式來定義屬性值。Item類的totalCost屬性和Cart類的subTotal屬性都被綁定到了計算其數值的表達式。無論何時,只要這些表達式所依賴的對象發生變化,其關聯的屬性值也將自動地被重新計算並更新。這讓我們想到了常見的電子表格:它含有包含公式(formulas)(公式中涉及到其它單元格中的數值)的單元格;當你在其它單元格中輸入數據時,那些包含此公式的表格值將被自動更新。

在HTML中的圖片

JavaFX的Label類實際上封裝了一個特定的JEditorPane類,後者使用了一個支持利用Java類裝載器(Java class loader)從JAR文件裝載圖片的、共享的圖片緩存。這樣就可以使用HTML <img>元素引用那些與應用打包在一起的圖片資源。

超鏈接

Label類也支持HTML超鏈接:將指定的URL作為HTML <a>元素的href屬性嵌入到label。

我們使用JavaFX #操作符來建立這樣的URL。#操作符生成一個字符串化對象引用(stringified object reference),此引用指向它的操作數,而操作數又可以使用JavaFX ?操作符間接引用。可能聽起來有些饒舌,我們可以在下面的講解中慢慢理解這段話的含義。例如:

var a = 20;
    var b = #a;
    assert b instanceof String; // passes
    var c = (Number) ?b;
    assert a == c; // passes

Label類的HTML渲染器在HTML <a href=url>上下文中識別這樣的URL,並處理鼠標在具有URL的元素上進行的點擊,如果URL的值指向一個函數或者操作的話,它將調用該函數或者操作。

例如,這裡使用超鏈接標簽代替按鈕來重寫前面的按鈕點擊示例:

class ButtonClickModel {
      attribute numClicks: Number;
    }
    var model = new ButtonClickModel();
    Frame {
      width: 200
      content: GridPanel {
        border: EmptyBorder {
          top: 30
          left: 30
          bottom: 30
          right: 30
        }
        rows: 2
        columns: 1
        vgap: 10
        cells:
        [Label {
           text: bind
               "<html>
                <a href='{#(operation() {model.numClicks++;})}'>
                  I'm a hyperlink!
                </a>
               </html>"
        },
        Label {
          text: bind "Number of clicks: {model.numClicks}"
        }]
      }
      visible: true
    };

上面示例中的粗體部分建立了一個新的operation,它將增加模型的numClicks屬性值。

並且這裡用到了前面講到的URL創建方式:應用#操作符來生成URL,而URL指向嵌入在HTML標識中的operation。

運行程序,顯示如下:

在點擊超鏈接兩次後,程序顯示變化為:

Group Panel(分組面板),Simple Label(簡單標簽)和TextField(文本欄)

本節將使用一個非常簡單的示例講解JavaFX的Group Panel、Simple Label和TextField類。

JavaFX GroupPanel類封裝了Java.net上的GroupLayout類。GroupLayout是一個強大的布局管理器,它將面板的內容表現為平行的水平、垂直分組的集合。在JavaFX中,這些平行的分組被簡單地稱為Row和Column。當你聲明一個GroupPanel時,你也可以針對每個水平、垂直的組件分組來聲明其Row和Column對象。然後,在添加組件時便可以將相應的Row和Column對象賦值給組件的row和column屬性。GroupPanel按照當前外觀風格准則在組件之間自動地插入間隔。通過聲明Row或者Column對象的alignment和resizable屬性,你能夠控制在行或者列中的組件對齊和行或者列是否可調整大小。

JavaFX TextField類封裝了Swing的JFormattedTextField。它具有一個value屬性,無論在焦點位於此文本框或者移到其它組件時,只要用戶按下Enter,該屬性值都將被更新。通過將數字賦值給它的columns,你可以控制它的寬度。而通過賦值LEADING、CENTER、TRAILING給它的horizontalAligment屬性,你還可以控制它的水平對齊。TextField類具有兩個值為函數的屬性,它們允許你執行基於用戶交互的行為:action和onChange。如果你將一個函數或者操作賦值給action屬性,無論何時用戶按下Enter鍵,此函數或者操作都會被調用。如果你將一個函數或者操作賦值給onChange屬性,當文本欄的value發生變化時,這個的函數或者操作將被調用。

JavaFX SimpleLabel類封裝了Swing的JLabel類。SimpleLabel與Label的不同之處在於它不支持超鏈接和首選大小(preferred size)。

下面顯示了一個示例:

下面是示例的代碼:

class Model {
      attribute firstName: String;
      attribute lastName: String;
    }
    var model = Model {
      firstName: "Joe"
      lastName: "Smith"
    };
    Frame {
      content: GroupPanel {
        var firstNameRow = Row { alignment: BASELINE }
        var lastNameRow = Row { alignment: BASELINE }
        var labelsColumn = Column {
          alignment: TRAILING
        }
        var fieldsColumn = Column {
          alignment: LEADING
          resizable: true
        }
        rows: [firstNameRow, lastNameRow]
        columns: [labelsColumn, fieldsColumn]
        content:
        [SimpleLabel {
          row: firstNameRow
          column: labelsColumn
          text: "First Name:"
        },
        TextField {
          row: firstNameRow
          column: fieldsColumn
          columns: 25
          value: bind model.firstName
        },
        SimpleLabel {
          row: lastNameRow
          column: labelsColumn
          text: "Last Name:"
        },
        TextField {
          row: lastNameRow
          column: fieldsColumn
          columns: 25
          value: bind model.lastName
        }]
      }
      visible: true
    };

上面的示例中關於布局的代碼顯示為藍色。本示例中的布局由兩行(一行用於first name,另一行用於last name)、兩列(一列用於標簽,另一列用於文本欄)組成。在GroupPanel的聲明中,四個變量(firstNameRow、lastNameRow、labelsColumn和fieldsColumn)被聲明為rows和columns屬性,即將兩行和兩列分別賦值給GroupPanel的rows和columns屬性。最後,正如你所見到的,label和TextField被賦值為GroupPanel的elements屬性。從label和TextField的聲明可以看出,它們的row和column也被相應地賦值。

Button(按鈕)

JavaFX Button類封裝了Swing的JButton組件。為了講解如何使用Button,讓我們從Swing教程中重建一個簡單的示例:

class ButtonDemoModel {
      attribute buttonEnabled: Boolean;
    }
    var model = ButtonDemoModel {
      buttonEnabled: true
    };
    Frame {
      title: "ButtonDemo"
      content: FlowPanel {
        content:
        [Button {
          text: "Disable middle button"
          verticalTextPosition: CENTER
          horizontalTextPosition: LEADING
          icon: Image {
             url: "http://java.sun.com/docs/books/tutorial/uiswing/examples/components/ButtonDemoProject/src/components/images/right.gif"
          }
          mnemonic: D
          toolTipText: "Click this button to disable the middle button"
          enabled: bind model.buttonEnabled
          action: operation() {
             model.buttonEnabled = false;
          }
        },
        Button {
          text: "Middle button"
          icon: Image {
            url: "http://java.sun.com/docs/books/tutorial/uiswing/examples/components/ButtonDemoProject/src/components/images/middle.gif"
          }
          verticalTextPosition: BOTTOM
          horizontalTextPosition: CENTER
          mnemonic: M
          toolTipText: "This middle button does nothing when you click it."
          enabled: bind model.buttonEnabled
        },
        Button {
          text: "Enable middle button"
          icon: Image {
            url: "http://java.sun.com/docs/books/tutorial/uiswing/examples/components/ButtonDemoProject/src/components/images/left.gif"
          }
          mnemonic: E
          toolTipText: "Click this button to enable the middle button"
          action: operation() {
             model.buttonEnabled = true;
          }
          enabled: bind not model.buttonEnabled
        }]
      }
      visible: true
    }

點擊左側按鈕後,程序出現以下變化:

示例程序中共有三個button,其中每個button的enabled屬性都綁定到模型對象的buttonEnabled屬性。當你通過觸發左側和右側按鈕的action修改此屬性時,這三個button的狀態都將發生變化。

我們通過將Image對象賦值給button的icon屬性為按鈕增添了圖片。

JavaFX Image對象具有一個url屬性,你可以將一個包含了指向圖片資源的URL作為其值。JavaFX具有內建的圖片緩存,它支持使用Java class loader從JAR文件裝載圖片。因此,我們能夠通過“file: URL”輕松地訪問和JAR文件一起打包的圖片資源。

TabbedPane(頁簽窗體)

為了演示如何使用TabbedPane,讓我們定義下面的具有TabbedPane組件相應屬性的模型類:Model。

class Model {
      attribute tabPlacement: TabPlacement;
      attribute tabLayout: TabLayout;
      attribute tabCount: Integer;
      attribute selectedTab: Integer;
    }

現在,讓我們從上面的模型出發設計一個TabbedPane示例。

var model = Model {
      tabPlacement: TOP
      tabLayout: WRAP
      selectedTab: 3
      tabCount: 5
    };
    Frame {
      height: 300
      width: 400
      content: TabbedPane {
        tabPlacement: bind model.tabPlacement
        tabLayout: bind model.tabLayout
        tabs: bind foreach (i in [1..model.tabCount])
          Tab {
            title: "Tab {i}"
            toolTipText: "Tooltip {i}"
          }
        selectedIndex: bind model.selectedTab
      }
      visible: true
    }

上面以粗體顯示的代碼展示了在TabbedPane和模型之間的依賴關系。在完成編碼後,TabbedPane的外觀將隨著模型的修改而改變。

我們通過將一組Tab對象賦值給TabbedPane的tabs屬性的方式將Tab添加到TabbedPane。TabPlacement和TabLayout類定義了一些枚舉值(TOP、LEFT、BOTTOM、RIGHT 、WRAP、SCROLL),我們可以將這些值相應地賦值給TabbedPane的tabPlacement和tabLayout屬性,從而能夠控制tab的位置和布局。TabbedPane的selectedIndex屬性表示了當前顯示哪個tab。

程序運行如下圖:

值得注意的是:在示例中第四個tab被選擇了,這是因為模型的selectedTab屬性被初始化為3。在本例中,TabbedPane的selectedIndex屬性也隨之更新,因為它被綁定到了模型的selectedTab屬性上。

對模型的tabPlacement屬性作出如下修改:

model.tabPlacement = BOTTOM;

tab將移動窗體的下方:

對模型的selectedTab屬性作出如下修改:

model.selectedTab = 0;

這將導致第一個tab被選擇:

對模型的tabCount屬性作出如下修改:

model.tabCount = 20;

這將導致15個新建的tab被添加到TabbedPane:

修改模型的tabLayout:

model.tabLayout = SCROLL;

程序運行效果如下圖:

修改模型的tabCount:

model.tabCount = 2;

結果只保留了前兩個tab:

ListBox(列表框)

JavaFX ListBox類提供了Swing JList組件的功能,但不同的是它提供了一個聲明式接口。

為了演示其用法,我們還是從Swing教程的ListDemo出發重建一個簡單示例:

在這個示例中,ListBox包含一個雇員姓名列表。如果點擊“Fire”按鈕,被選擇的雇員將從列表中移除。如果在列表下方的文本框中輸入新姓名,那麼“Hire”按鈕將變為可用狀態。如果此時按下“Hire”按鈕,這個新的姓名將被添加到列表。

這個示例也演示了如何使用BorderPanel和FlowPanel。一個BorderPanel最多包括五個組件,這五個組件將被放置在面板的上方、左側、下方、右側或者中央。它會垂直拉伸左側、右側的組件,水平拉伸上方、下方的組件,而位於中央的組件將向垂直、水平兩個方向伸展。FlowPanel包括了一個按照從左到右的順序放置組件的列表,就像在段落中文本一樣。而且本示例還展示了如何使用RigidArea:一種用於在其它組件之間創建空白的、不可見的填充器組件。

class EmployeeModel {
      attribute employees: String*;
      attribute selectedEmployee: Number;
      attribute newHireName: String;
    }
    var model = EmployeeModel {
      employees:
      ["Alan Sommerer",
       "Alison Huml",
       "Kathy Walrath",
       "Lisa Friendly",
       "Mary Campione",
       "Sharon Zakhour"]
    };
    Frame {
      title: "ListBox Example"
      content: BorderPanel {
        center: ListBox {
          selection: bind model.selectedEmployee
          cells: bind foreach (emp in model.employees)
            ListCell {
              text: emp
            }
        }
        bottom: FlowPanel {
          content:
          [Button {
            text: "Fire"
            action: operation() {
              delete model.employees[model.selectedEmployee];
            }
          },
          RigidArea {
            width: 5
          },
          TextField {
            columns: 30
            value: bind model.newHireName
          },
          RigidArea {
            width: 5
          },
          Button {
            text: "Hire"
            enabled: bind model.newHireName.length() > 0
            action: operation() {
              insert model.newHireName
                after model.employees[model.selectedEmployee];
              model.newHireName = "";
              if (sizeof model.employees == 1) {
                model.selectedEmployee = 0;
              } else {
                model.selectedEmployee++;
              }
            }
          }]
        }
      }
      visible: true
    }

上面示例中的粗體代碼用於創建ListBox。我們通過將一組ListCell對象賦值到ListBox的cells屬性來創建ListBox。而cells就取值於模型的雇員列表。因此,當從模型中添加或者刪除雇員時,相應的單元格將被添加到ListBox或者從ListBox中刪除。當單元格被渲染時,你為ListCell的text屬性所賦的值也將被顯示出來。盡管在本示例中沒有必要,但你仍然可以將一些HTML代碼賦值給ListCell的text屬性,從而建立一個風格化文本和(或者)圖片來作為單元格的內容。

ListBox的selection屬性包含了被選擇單元格的索引。在本例中,它被綁定到模型的selectedEmployee屬性,因此當在列表中改變被選項時,模型的selectedEmployee屬性也將更新。與此同時,如果selectedEmployee屬性被更新,列表的被選項也會作出相應改變。這正是示例中“Hire”按鈕的action所做的。

在點擊“Fire”按鈕兩次後,程序將改變為下圖:

如果在文本欄中輸入新的名字,則程序將發生改變:

接著點擊“Hire”按鈕:

splitPane(分割窗體)

JavaFX SplitPane類基於一個自定義Java組件,而不是Swing的JSplitPane類。與JSplitPane不同的是:它能夠包括多個組件。而和JSplitPane一樣是:你可以通過為它所包含的組件賦值來控制它的朝向和空間的數量。讓我們看看下面的示例:

這個示例由一個水平分割的窗體和兩個組件組成。左側的組件是一個ListBox,它占據了30%的空間。而右側的組件是一個包含CenterPanel(一種含有一個位於其中央區域的組件的面板)的ScrollPane。CenterPanel包含了一個SimpleLabel,後者用於顯示與被選擇的列表項相關的圖片。

class ExampleModel {
      attribute imageFiles: String*;
      attribute selectedImageIndex: Number;
      attribute selectedImageUrl: String;
    }
    var model = ExampleModel {
      var: self
      imageFiles: ["Bird.gif", "Cat.gif", "Dog.gif",
             "Rabbit.gif", "Pig.gif", "dukeWaveRed.gif",
             "kathyCosmo.gif", "lainesTongue.gif",
             "left.gif", "middle.gif", "right.gif",
             "stickerface.gif"]
      selectedImageUrl: bind "http://java.sun.com/docs/books/tutorial/uiswing/examples/components/SplitPaneDemoProject/src/components/images/{self.imageFiles[self.selectedImageIndex]}"
    };
    Frame {
      title: "SplitPane Example"
      height: 400
      width: 500
      content: SplitPane {
        orientation: HORIZONTAL
        content:
        [SplitView {
          weight: 0.30
          content: ListBox {
             selection: bind model.selectedImageIndex
             cells: bind foreach (file in model.imageFiles)
              ListCell {
                text: bind file
              }
          }
        },
        SplitView {
          weight: 0.70
          content: ScrollPane {
            view: CenterPanel {
              background: white
              content: SimpleLabel {
                icon: Image {url: bind model.selectedImageUrl}
              }
            }
          }
        }]
      }
      visible: true
    }

示例中的粗體代碼是與splitPane相關的。正如你見到的那樣,splitPane的orientation屬性被賦值為HORIZONTAL。通過將一組SplitView對象賦值給content屬性,我們便為splitPane添加了組件。每個SplitView具有兩個屬性:weight和content。weight屬性決定了當分割窗體被調整大小時應有多少的空間分配給它(SplitView)。而你為content的賦值將顯示在splitPane中。

RadioButton(單選按鈕)、RadioButtonMenuItem(單選按鈕菜單項)、ToggleButton(開關按鈕)和ButtonGroup(按鈕分組)

JavaFX RadioButton類封裝了Swing的JRadioButton組件。RadioButtonMenuItem類封裝了Swing的JRadioButtonMenuItem組件。而ToggleButton類封裝了Swing的JToggleButton組件。

在這些組件之間、以及它們與單項選擇的列表框(ListBox)、下拉列表框(ComboBox)、頁簽面板(TabbedPane)、卡片面板(CardPanel)之間具有很強的相似性,即所有這些組件都提供了從選項列表中挑選其中一個選項的能力。

RadioButtons、RadioButtonMenuItems和ToggleButtons都與一個使用JavaFX ButtonGroup類的選項列表相關,而JavaFX ButtonGroup類與Swing ButtonGroup相對應。與Swing類不同的是,JavaFX ButtonGroup提供了一個和單選列表框相似的選擇模型。ButtonGroup的selection屬性保存了一個用來控制被選擇按鈕的數字索引。如果你為此屬性賦值,那麼索引值為此值的按鈕將被選擇,而其它按鈕也將取消選擇。如果你選擇某個按鈕,這個按鈕的索引將隱含地被賦值給ButtonGroup的selection屬性。

為了演示,讓我們對前面一節的示例進行擴展,使其包含ButtonGroup。首先在菜單中放置一組RadioButtonMenuItems作為菜單項。接著,將一組RadioButtons放置到一個四列的GridPanel中。最後,在一個單列GridPanel中放置一組ToggleButtons。每個按鈕的分組都將像前一節示例中的ListBox的cells那樣從同一個模型中“投影”出來,並且它們的ButtonGroup的selection屬性也像ListBox的selection屬性那樣被綁定到同一個模型屬性。你在ListBox、Menu、RadioButton、ToggleButton中作出選擇都將影響到其關聯對象。

聽起來難免比較抽象,還是讓我們看下面的示例程序初始化界面和源代碼吧:

class ExampleModel {
      attribute imageFiles: String*;
      attribute selectedImageIndex: Number;
      attribute selectedImageUrl: String;
    }
    var model = ExampleModel {
      var: self
      imageFiles: ["Bird.gif", "Cat.gif", "Dog.gif",
             "Rabbit.gif", "Pig.gif", "dukeWaveRed.gif",
             "kathyCosmo.gif", "lainesTongue.gif",
             "left.gif", "middle.gif", "right.gif",
             "stickerface.gif"]
      selectedImageUrl: bind
        "http://java.sun.com/docs/books/tutorial/uiswing/examples/components/SplitPaneDemoProject/src/components/images/{self.imageFiles[self.selectedImageIndex]}"
    };
    Frame {
      menubar: MenuBar {
        menus: Menu {
          text: "File"
          mnemonic: F
          var buttonGroup = ButtonGroup {
            selection: bind model.selectedImageIndex
          }
          items: foreach (imageName in model.imageFiles)
            RadioButtonMenuItem {
              buttonGroup: buttonGroup
              text: imageName
            }
        }
      }
      title: "RadioButton/ToggleButton Example"
      height: 400
      width: 500
      content: BorderPanel {
        top: GridPanel {
            rows: sizeof model.imageFiles / 4
            columns: sizeof model.imageFiles % 4
            var buttonGroup = ButtonGroup {
              selection: bind model.selectedImageIndex
            }
            cells: foreach (imageName in model.imageFiles)
               RadioButton {
                 buttonGroup: buttonGroup
                 text: imageName
               }
        }
        right: GridPanel {
            rows: sizeof model.imageFiles
            columns: 1
            var buttonGroup = ButtonGroup {
               selection: bind model.selectedImageIndex
            }
            cells: foreach (imageName in model.imageFiles)
               ToggleButton {
                 buttonGroup: buttonGroup
                 text: imageName
               }
        }
        center: SplitPane {
          orientation: HORIZONTAL
          content:
          [SplitView {
            weight: 0.30
            content: ListBox {
               selection: bind model.selectedImageIndex
               cells: bind foreach (imageName in model.imageFiles)
                 ListCell {
                  text: bind imageName
                 }
             }
          },
          SplitView {
             weight: 0.70
             content: ScrollPane {
               view: CenterPanel {
                 background: white
                 content: SimpleLabel {
                   icon: Image {url: bind model.selectedImageUrl}
                 }
               }
             }
          }]
        }
      }
      visible: true
    }

示例中的橙色代碼是與ButtonGroups相關的。正如你所見到的,我們通過將button的buttonGroup屬性設置為指定的ButtonGroup,將一組button添加到了buttonGroup中。

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