無為

          無為則可為,無為則至深!

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            190 Posts :: 291 Stories :: 258 Comments :: 0 Trackbacks

          本文檔將討論在JavaFX語言中可用的各種GUI組件,并提供示例代碼,討論JavaFX組件與Swing GUI組件相比的差異。

          前提條件

          如果你從前沒有閱讀過JavaFX腳本語言起步教程,那么我們強烈推薦你在繼續(xù)閱讀本文檔之前首先閱讀JavaFX腳本語言起步教程

          你應(yīng)該具有熟知Java編程語言,尤其是在Swing和Java2D方面。

          內(nèi)容

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

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

                  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程序,它和前面的代碼具有相同的效果。

          下面列舉出在聲明式方法和過程式方法中實際發(fā)生了什么:

          1. 調(diào)用Frame類構(gòu)造方法建立新的Frame。
          2. 給Frame的title、width、visible和content屬性賦值。
          3. 在賦值content屬性的過程中,調(diào)用了Label類的構(gòu)造方法建立一個新的Label,并且為它的text屬性賦值。

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

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

          top

          增加動態(tài)行為

          目前,我們編寫的“Hello World”程序并沒有動態(tài)行為。為了使用JavaFX建立一個具有動態(tài)行為的圖形用戶接口組件,你可以建立這樣的圖形用戶接口:它的屬性依賴于其它對象的屬性值。而這些其它的對象將成為應(yīng)用狀態(tài)的表象(representation),即它們代表了應(yīng)用的狀態(tài)。由于這個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
          };

          此程序運行結(jié)果如下圖:

          HelloWorld window #2

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

                  model.saying = "Goodbye Cruel World!";

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

          view changes to goodbye

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

          在例如Buttons、CheckBoxes、TextFields這些輸入部件中,處于模型屬性和GUI組件屬性之間的關(guān)聯(lián)可以是雙向的。

          請考慮以下示例:

                  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
          };

          此程序運行結(jié)果如下圖:

          如果你在TextField中輸入其它的文字,并按下Enter,那么窗體的標題將相應(yīng)地改變:

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

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

          top

          學習更多的JavaFX GUI組件

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

          top

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

          在JavaFX語言中,使用Border和Layout Manager也采用聲明的方式。每個Swing/AWT Layout Manager都被封裝在一個JavaFX類中,這個類使用指定的Layout Manager來實例化JPanel。被添加到JPanel上的組件被聲明為這個JavaFX類的屬性。每個Swing的Border類型也同樣被封裝在 一個JavaFX類中,這個類具有與Swing Border配置選項對應(yīng)的屬性。這里提供了一個使用EmptyBorderGridPanel的簡單示例。正如你所期待的那樣,JavaFX的EmptyBorder對應(yīng)著javax.swing.border.EmptyBorder,而GridPanel則對應(yīng)著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
          };

          程序運行結(jié)果如下圖:

          在點擊按鈕多次后,將出現(xiàn)如下效果:

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

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

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

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

          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類和其相應(yīng)的Swing Border類的對應(yīng)表格:

          JavaFX Border Swing Border
          EmptyBorder EmptyBorder
          LineBorder LineBorder
          BevelBorder BevelBorder
          SoftBevelBorder SoftBevelBorder
          MatteBorder MatteBorder
          TitledBorder TitledBorder

          top

          Menu(菜單)

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

                  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
          }

          在程序執(zhí)行后,按下ALT+F組合鍵將出現(xiàn)如下情形:

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

          為了定義菜單,需要賦值menu的textmnemonicitems的屬性值。

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

          這里唯一的菜單項是一個text屬性和值為Xmnemonic屬性。而它的accelerator屬性也被賦值了。注意:在聲明中的類型名Accelerator被省略了。這在JavaFX中是允許的。如果類型名沒有提供,該屬性的靜態(tài)類型將被使用,在本例中accelerator屬性的靜態(tài)類型是Accelerator。另外,acceleratormodifierkeyStroke屬性都使用枚舉值進行了初始化。

          最后,MenuItemaction屬性是function類型的(即指它的值為function,而非對象)。在本例中,action屬性是一個內(nèi)聯(lián)(inline)的operation,它調(diào)用了一些Java代碼。

          top

          Label(標簽)

          JavaFX的Label class支持HTML內(nèi)容。通過使用Label,你可以利用HTML和CSS建立風格化文本(styled text)和圖片,它非常類似編寫典型的Web應(yīng)用。另外,通過使用JavaFX內(nèi)嵌表達式,你能夠在Swing應(yīng)用中建立動態(tài)HTML內(nèi)容,這就像在編寫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
          }

          程序執(zhí)行結(jié)果如下圖:

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

                 delete cart.items;

          程序?qū)@示如下結(jié)果:

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

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

          top

          在HTML中的圖片

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

          超鏈接

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

          我們使用JavaFX #操作符來建立這樣的URL。#操作符生成一個字符串化對象引用(stringified object reference),此引用指向它的操作數(shù),而操作數(shù)又可以使用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的值指向一個函數(shù)或者操作的話,它將調(diào)用該函數(shù)或者操作。

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

                  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創(chuàng)建方式:應(yīng)用#操作符來生成URL,而URL指向嵌入在HTML標識中的operation。

          運行程序,顯示如下:

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

          top

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

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

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

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

          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
          };

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

          top

          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
          }

          點擊左側(cè)按鈕后,程序出現(xiàn)以下變化:

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

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

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

          top

          TabbedPane(頁簽窗體)

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

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

          現(xiàn)在,讓我們從上面的模型出發(fā)設(shè)計一個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和模型之間的依賴關(guān)系。在完成編碼后,TabbedPane的外觀將隨著模型的修改而改變。

          我們通過將一組Tab對象賦值給TabbedPane的tabs屬性的方式將Tab添加到TabbedPane。TabPlacementTabLayout類定義了一些枚舉值(TOP、LEFT、BOTTOM、RIGHT 、WRAP、SCROLL),我們可以將這些值相應(yīng)地賦值給TabbedPane的tabPlacementtabLayout屬性,從而能夠控制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;

          結(jié)果只保留了前兩個tab:

          top

          ListBox(列表框)

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

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

          在這個示例中,ListBox包含一個雇員姓名列表。如果點擊“Fire”按鈕,被選擇的雇員將從列表中移除。如果在列表下方的文本框中輸入新姓名,那么“Hire”按鈕將變?yōu)榭捎脿顟B(tài)。如果此時按下“Hire”按鈕,這個新的姓名將被添加到列表。

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

                  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
          }

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

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

          在點擊“Fire”按鈕兩次后,程序?qū)⒏淖優(yōu)橄聢D:

          如果在文本欄中輸入新的名字,則程序?qū)l(fā)生改變:

          接著點擊“Hire”按鈕:


          top

          splitPane(分割窗體)

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

          這個示例由一個水平分割的窗體和兩個組件組成。左側(cè)的組件是一個ListBox,它占據(jù)了30%的空間。而右側(cè)的組件是一個包含CenterPanel(一種含有一個位于其中央?yún)^(qū)域的組件的面板)的ScrollPane。CenterPanel包含了一個SimpleLabel,后者用于顯示與被選擇的列表項相關(guān)的圖片。

                  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相關(guān)的。正如你見到的那樣,splitPane的orientation屬性被賦值為HORIZONTAL。通過將一組SplitView對象賦值給content屬性,我們便為splitPane添加了組件。每個SplitView具有兩個屬性:weightcontentweight屬性決定了當分割窗體被調(diào)整大小時應(yīng)有多少的空間分配給它(SplitView)。而你為content的賦值將顯示在splitPane中。

          top

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

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

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

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

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

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

                  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相關(guān)的。正如你所見到的,我們通過將button的buttonGroup屬性設(shè)置為指定的ButtonGroup,將一組button添加到了buttonGroup中。

          如果點擊在ListBox中的“Pig.gif”(或者選擇“Pig.gif”的RadioButton或ToggleButton),將出現(xiàn)下面的變化:

          如果打開菜單,你將看到它也發(fā)生了同樣的變化:


          top

          ComboBoxes(下列選擇框)

          JavaFX ComboBox與Swing JComboBox組件相關(guān)。我們將在上一個示例中添加兩個組件來演示如何使用ComboBox。示例代碼如下:

                  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
          }
          function makeRadioButton(buttonGroup, imageName) {
          return RadioButtonMenuItem {
          buttonGroup: buttonGroup
          text: imageName
          };
          }
          items: foreach (imageName in model.imageFiles)
          makeRadioButton(buttonGroup, imageName)

          }
          }
          title: "RadioButton/ToggleButton/ComboBox 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: BorderPanel {
          top: ComboBox {
          selection: bind model.selectedImageIndex
          cells: bind foreach (imageName in model.imageFiles)
          ComboBoxCell {
          text: bind imageName
          }
          }

          center: ScrollPane {
          view: CenterPanel {
          background: white
          content: SimpleLabel {
          icon: Image {url: bind model.selectedImageUrl}
          }
          }
          }
          }
          }]
          }
          bottom: FlowPanel {
          alignment: LEADING
          content: ComboBox {
          selection: bind model.selectedImageIndex
          cells: bind foreach (imageName in model.imageFiles)
          ComboBoxCell {
          text: bind "<html>
          <table>
          <tr>
          <td>
          <img src='http://java.sun.com/docs/books/tutorial/uiswing/examples/components/SplitPaneDemoProject/src/components/images/{imageName}' height='32' width='32'></img>
          </td>
          <td>
          {imageName}
          </td>
          </tr>
          </table>
          </html>"
          }
          }

          }
          }
          visible: true
          }

          上例中有關(guān)ComboBox的代碼表示為粗體。我們通過將一組ComboBoxCell對象賦值給ComboBox的cells屬性,來為ComboBox賦予下拉列表項。ComboBoxCelltext屬性決定了下拉列表單元的外觀。當然,你還可以建立風格化文本或者圖片作為內(nèi)容的下拉列表項:將包含這些風格化文本或者圖片的HTML代碼賦值給text屬性(就像示例中左下方的ComboBox展示的那樣)。ComboBoxselection屬性決定了哪個列表項被選擇。將一個整數(shù)(從0開始)索引賦值到這個屬性,將使這個索引對應(yīng)位置的列表項被選中。在用戶選擇列表項的同時,被選擇的列表項的索引值將被隱含地賦值給selection屬性。在上例中的兩個ComboBox中,selection屬性都被綁定到同一個模型屬性。同樣的,ComboBox中的列表項(cells)也從同一個模型屬性“投影”出來。作為結(jié)果,你能夠通過ComboBox、listbox、button groups來選擇被顯示的圖片。

          如果打開第二個ComboBox,示例程序?qū)⒆優(yōu)椋?/p>


          top

          Trees(樹形)

          JavaFX Tree類提供了一個封裝了Swing JTree組件的聲明式接口。首先,讓我們一起通過建立一個沒有動態(tài)行為的簡單示例來了解Tree的用法:

                  Frame {
          height: 400
          width: 300
          content: Tree {
          root: TreeCell {
          text: "Tree"
          cells:
          [TreeCell {
          text: "colors"
          cells:
          [TreeCell {
          text: "<html><font color='blue'>blue</font></html>"
          },
          TreeCell {
          text: "<html><font color='red'>red</font></html>"
          },
          TreeCell {
          text: "<html><font color='green'>green</font></html>"
          }]
          },
          TreeCell {
          text: "food"
          cells:
          [TreeCell {
          text: "hot dogs"
          },
          TreeCell {
          text: "pizza"
          },
          TreeCell {
          text: "ravioli"
          }]
          }]
          }
          }
          visible: true
          }

          上面的代碼運行結(jié)果如下:

          為了構(gòu)造Tree,我們將一個返回TreeCell對象的表達式被賦值給它的root(根)屬性。TreeCell代表了Tree的一行。你可以將一組TreeCell對象賦值給它的cells屬性來描述某個TreeCell的子單元(child cells)。另外,每個TreeCell都具有一個決定其外觀的text屬性。你也可以將HTML代碼賦值給text屬性來建立一個風格化文本或者圖片作為內(nèi)容的TreeCell。

          接下來,讓我們重建一個Swing教程中的示例(GenealogyExample),它顯示了某人的后代或者父輩情況。

          當我們運行這個示例后,程序?qū)@示以下:

          如果在Tree中選擇某人并點擊某個單選按鈕中,那么這個被選擇的人將成為Tree的根。Tree將根據(jù)選擇將此人的父輩或者后代顯示在其子節(jié)點中。

          下面是示例代碼。其中與Tree有關(guān)的代碼以粗體顯示。TreeCell具有一個selected屬性(Boolean類型),它決定了自身是否被選擇。與此同時,如果你通過程序?qū)⒁粋€Boolean值賦值給這個屬性的話,相應(yīng)的TreeCell將依照selected屬性值被選擇或者取消選擇。

          在示例中,由于家譜是一個遞歸的數(shù)據(jù)結(jié)構(gòu),于是我們需要使用一個能夠被遞歸調(diào)用的表達式來定義TreeCell的cells屬性。請注意:在這里我們使用了bind lazy操作符而不是在初始化cells屬性中使用的bind,因為它標識了lazy式求值。這意味著直到它左側(cè)表達式第一次被訪問到時,其右側(cè)表達式才被求值。因此,對descendantTree()ancestorTree()函數(shù)的遞歸調(diào)用并非馬上執(zhí)行,而是直到你展開Tree中的某個節(jié)點,Tree要求訪問子節(jié)點的cells時才被執(zhí)行。

                  class GeneologyModel {
          attribute people: Person*;
          attribute selectedPerson: Person;
          attribute showDescendants: Boolean;
          }

          class Person {
          attribute selected: Boolean;
          attribute father: Person;
          attribute mother: Person;
          attribute children: Person*;
          attribute name: String;
          }

          // By defining these triggers I can populate the model
          // by just assigning the mother and father attributes of a Person

          trigger on Person.father = father {
          insert this into father.children;
          }

          trigger on Person.mother = mother {
          insert this into mother.children;
          }

          // Create and populate the model
          var model = GeneologyModel {

          var jack = Person {
          selected: true
          name: "Jack (great-granddaddy)"
          }
          var jean = Person {
          name: "Jean (great-granny)"
          }
          var albert = Person {
          name: "Albert (great-granddaddy)"
          }
          var rae = Person {
          name: "Rae (great-granny)"
          }
          var paul = Person {
          name: "Paul (great-granddaddy)"
          }
          var josie = Person {
          name: "Josie (great-granny)"
          }
          var peter = Person {
          father: jack
          mother: jean
          name: "Peter (grandpa)"
          }
          var zoe = Person {
          father: jack
          mother: jean
          name: "Zoe (grandma)"
          }
          var simon = Person {
          father: jack
          mother: jean
          name: "Simon (grandpa)"
          }
          var james = Person {
          father: jack
          mother: jean
          name: "James (grandpa)"
          }
          var bertha = Person {
          father: albert
          mother: rae
          name: "Bertha (grandma)"
          }
          var veronica = Person {
          father: albert
          mother: rae
          name: "Veronica (grandma)"
          }
          var anne = Person {
          father: albert
          mother: rae
          name: "Anne (grandma)"
          }
          var renee = Person {
          father: albert
          mother: rae
          name: "Renee (grandma)"
          }
          var joseph = Person {
          father: paul
          mother: josie
          name: "Joseph (grandpa)"
          }
          var isabelle = Person {
          father: simon
          mother: veronica
          name: "Isabelle (mom)"
          }
          var frank = Person {
          father: simon
          mother: veronica
          name: "Frank (dad)"
          }
          var louis = Person {
          father: simon
          mother: veronica
          name: "Louis (dad)"
          }
          var laurence = Person {
          father: james
          mother: bertha
          name: "Laurence (dad)"
          }
          var valerie = Person {
          father: james
          mother: bertha
          name: "Valerie (mom)"
          }
          var marie = Person {
          father: james
          mother: bertha
          name: "Marie (mom)"
          }
          var helen = Person {
          father: joseph
          mother: renee
          name: "Helen (mom)"
          }
          var mark = Person {
          father: joseph
          mother: renee
          name: "Mark (dad)"
          }
          var oliver = Person {
          father: joseph
          mother: renee
          name: "Oliver (dad)"
          }
          var clement = Person {
          father: laurence
          mother: helen
          name: "Clement (boy)"
          }
          var colin = Person {
          father: laurence
          mother: helen
          name: "Colin (boy)"
          }

          people: [jack, jean, albert, rae, paul, josie,
          peter, zoe, simon, james, bertha, anne,
          renee, joseph, frank, louis, laurence,
          valerie, marie, helen, mark, oliver,
          clement, colin]

          selectedPerson: jack
          showDescendants: true
          };

          // Tree generation functions:
          operation geneologyTree(p:Person, showDescendants:Boolean) {
          if (showDescendants) {
          return descendantTree(p);
          } else {
          return ancestorTree(p);
          }
          }

          function descendantTree(p:Person) {
          return TreeCell {
          selected: bind p.selected
          text: bind p.name
          cells:
          bind lazy
          foreach (c in p.children)
          descendantTree(c)
          }
          ;
          }

          function ancestorTree(p:Person) {
          return TreeCell {
          selected: bind p.selected
          text: bind p.name
          cells:
          bind lazy
          foreach (a in [p.father, p.mother])
          ancestorTree(a)
          }
          ;
          }

          Frame {
          title: "Genology Example"
          height: 300
          width: 300
          content: BorderPanel {
          top: FlowPanel {
          var buttonGroup = new ButtonGroup()
          content:
          [RadioButton {
          buttonGroup: buttonGroup
          text: "Show Descendants"
          selected: model.showDescendants
          onChange: operation(newValue:Boolean) {
          if (newValue) {
          var selectedPerson = model.people[selected];
          if (selectedPerson <> null) {
          model.selectedPerson = selectedPerson;
          }
          model.showDescendants = true;
          }
          }
          },
          RadioButton {
          buttonGroup: buttonGroup
          text: "Show Ancestors"
          onChange: operation(newValue:Boolean) {
          if (newValue) {
          var selectedPerson = model.people[selected];
          if (selectedPerson <> null) {
          model.selectedPerson = selectedPerson;
          }
          model.showDescendants = false;
          }
          }
          }]
          }
          center: Tree {
          showRootHandles: true
          root: bind geneologyTree(model.selectedPerson,
          model.showDescendants)
          }

          }
          visible: true
          }

          當所有節(jié)點都被展開并且選擇“Clement”節(jié)點時,Tree將形如下圖:

          在點擊“Show Ancestors”后,Clement將成為根,他的雙親將顯示在他的下面:


          top

          Tables(表格)

          JavaFX Table類封裝了Swing JTable組件。我們在這里通過對Swing教程示例(SimpleTableDemo)進行微小的修改來示范如何使用Table

          創(chuàng)建示例表格的代碼如下:

                  class Person {
          attribute firstName: String;
          attribute lastName: String;
          attribute sport: String;
          attribute numYears: Number;
          attribute vegetarian: Boolean;
          attribute selected: Boolean;
          }

          class TableDemoModel {
          attribute people: Person*;
          }

          var model = TableDemoModel {
          people:
          [Person {
          firstName: "Mary"
          lastName: "Campione"
          sport: "Snowboarding"
          numYears: 5
          vegetarian: false
          },
          Person {
          firstName: "Alison"
          lastName: "Huml"
          sport: "Rowing"
          numYears: 3
          vegetarian: true
          },
          Person {
          firstName: "Kathy"
          lastName: "Walrath"
          sport: "Knitting"
          numYears: 2
          vegetarian: false
          },
          Person {
          firstName: "Sharon"
          lastName: "Zakhour"
          sport: "Speed reading"
          numYears: 20
          vegetarian: true
          },
          Person {
          firstName: "Philip"
          lastName: "Milne"
          sport: "Pool"
          numYears: 10
          vegetarian: false
          }]
          };

          Frame {
          height: 120
          width: 500
          title: "SimpleTableDemo"
          content: Table {
          columns:
          [TableColumn {
          text:
          "First Name"
          },
          TableColumn {
          text:
          "Last Name"
          },
          TableColumn {
          text:
          "Sport"
          width:
          100

          },
          TableColumn {
          text:
          "# of Years"
          alignment:
          TRAILING
          },
          TableColumn {
          text:
          "Vegetarian"
          alignment:
          CENTER
          }]

          cells: bind
          foreach (p in model.people)

          [TableCell {
          text:
          bind p.firstName
          selected: bind p.selected
          },
          TableCell {
          text:
          bind p.lastName
          },
          TableCell {
          text:
          bind p.sport

          },
          TableCell {
          text:
          bind "{p.numYears}"
          },
          TableCell {
          text:
          bind if p.vegetarian then "Yes" else "No"
          toolTipText:
          bind "{p.firstName} {p.lastName} {if not p.vegetarian then "eats" else "does not eat"} meat"
          }]

          }
          visible: true
          }

          上例與table相關(guān)的代碼表示為粗體。為了建立Table,我們需要將一組TableColumn對象賦值給Tablecolumns屬性,并把一組TableCell對象賦值給它的cells屬性。在上例中,由于我們把五個TableColumn賦值給了Table,所以table中顯示了五列。同理,由于我們?yōu)槊總€person賦值了五個TableCell(分別對應(yīng)person的5個屬性),從而使每個person的信息正好完整地顯示在每一行。TableColumntext屬性決定了列頭部單元格的內(nèi)容。它的widthalignment屬性決定了該列的首選寬度和水平對齊。

          由于JavaFX Table是一個ScrollableWidget部件,因此你無需給它添加滑動面板。

          top

          Text Components(文本組件)

          我們在這里通過對Swing教程示例進行微小的修改來示范如何使用文本組件:

          JavaFX文本組件與Swing組件之間的對應(yīng)關(guān)系如下:

          JavaFX部件 Swing組件
          TextField JFormattedTextField
          PasswordField JPasswordField
          TextArea JTextArea
          EditorPane JEditorPane
          TextPane JTextPane

           

                  class TextSamplerModel {
          attribute textFieldInput: String?;
          }

          var model = TextSamplerModel {
          };

          Frame {
          title: "Text Sampler"
          visible: true
          content: SplitPane {
          orientation: HORIZONTAL
          content:
          [SplitView {
          weight: 0.5
          content:
          BorderPanel {
          top: GridBagPanel {
          border: CompoundBorder {
          borders:
          [TitledBorder {
          title: "Text Fields"
          },
          EmptyBorder {
          top: 5
          left: 5
          bottom: 5
          right: 5
          }]
          }
          cells:
          [GridCell {
          anchor: EAST
          gridx: 0
          gridy: 0
          content: SimpleLabel {
          text: "TextField: "
          }
          },
          GridCell {
          anchor: WEST
          fill: HORIZONTAL
          weightx: 1
          gridx: 1
          gridy: 0
          content: TextField {
          action: operation(value:String) {
          model.textFieldInput = value;
          }
          }

          },
          GridCell {
          anchor: EAST
          gridx: 0
          gridy: 1
          insets: {top: 2}
          content: SimpleLabel {
          text: "PasswordField: "
          }
          },
          GridCell {
          gridx: 1
          gridy: 1
          fill: HORIZONTAL
          weightx: 1
          insets: {top: 2}
          content: PasswordField {
          action: operation(value:String) {
          model.textFieldInput = value;
          }
          }

          },
          GridCell {
          anchor: WEST
          weightx: 1.0
          gridx: 0
          gridy: 2
          gridwidth: 2
          fill: HORIZONTAL
          content: SimpleLabel {
          border: EmptyBorder {
          top: 10
          }
          text: bind if model.textFieldInput == null then "Type text and then Return in a field" else "You typed \"{model.textFieldInput}\""

          }
          }]
          }
          center: BorderPanel {
          border: CompoundBorder {
          borders:
          [TitledBorder {
          title: "Plain Text"
          },
          EmptyBorder {
          top: 5
          left: 5
          bottom: 5
          right: 5
          }]
          }
          center: TextArea {
          font: new Font("Serif", Font.ITALIC, 16)
          lineWrap: true
          wrapStyleWord: true
          text: "This is an editable TextArea that has been initialized with its text attribute. A text area is a \"plain\" text component, which means that although it can display text in any font, all of the text is in the same font"
          }

          }
          }
          },
          SplitView {
          weight: 0.5
          content: SplitPane {
          border: CompoundBorder {
          borders:
          [TitledBorder {
          title: "Styled Text"
          },
          EmptyBorder {
          top: 5
          left: 5
          bottom: 5
          right: 5
          }]
          }
          orientation: VERTICAL
          content:
          [SplitView {
          weight: 0.5
          content: EditorPane {
          opaque: true
          preferredSize: {height: 250 width: 250}
          contentType: HTML
          editable: false
          text: "<html>
          <body>
          <img src='http://java.sun.com/docs/books/tutorial/uiswing/examples/components/SplitPaneDemoProject/src/components/images/dukeWaveRed.gif' width='64' height='64'>
          This is an uneditable <code>EditorPane</code>,
          which was <em>initialized</em>
          with <strong>HTML</strong> text <font size='-2'>but not from</font> a
          <font size='+2'>URL</font>.

          <p>
          An editor pane uses specialized editor kits
          to read, write, display, and edit text of
          different formats.
          </p>
          <p>
          The Swing text package includes editor kits
          for plain text, HTML, and RTF.
          </p>
          <p>
          You can also develop
          custom editor kits for other formats.
          </p>
          </body></html>"
          }

          },
          SplitView {
          weight: 0.5
          content: TextPane {
          preferredSize: {height: 250 width: 250}
          editable: true
          content:
          ["This is an editable TextPane, another styled text component, which supports embedded icons...\n",
          Image {url: "http://java.sun.com/docs/books/tutorial/uiswing/components/example-swing/images/Pig.gif"},
          "\n...and embedded components...\n",
          Button {
          contentAreaFilled: false
          icon: Image {url: "http://java.sun.com/docs/books/tutorial/uiswing/components/example-swing/images/sound.gif"}
          },
          "\nTextPane is a subclass of EditorPane that uses a StyledEditorKit and StyledDocument,\n and provides cover methods for interacting with those objects."]
          }

          }]
          }
          }]
          }
          }

          top

          Spinners(微調(diào)控制器)和Sliders(滑動條)

          JavaFX SpinnerSlider類與Swing組件之間對應(yīng)關(guān)系如下:

          JavaFX部件 Swing組件
          Spinner JSpinner

           

          讓我們通過建立一個展示攝氏和華氏之間換算關(guān)系的、簡單的應(yīng)用來演示如何使用它們吧:

                  class Temp {
          attribute celsius: Number;
          attribute farenheit: Number;
          attribute showCelsius: Boolean;
          attribute showFarenheit: Boolean;
          }


          trigger on Temp.celsius = value {
          farenheit = (9/5 * celsius + 32);
          }

          trigger on Temp.farenheit = value {
          celsius = ((farenheit - 32) * 5/9);
          }


          Frame {

          var temp = Temp {
          farenheit: 32
          showFarenheit: true
          showCelsius: true
          }

          height: 300

          width: 400


          title: "Temperature"

          content: Box {
          orientation: VERTICAL
          content:
          [FlowPanel {
          content:
          [CheckBox {
          text: "Show Celsius"
          selected: bind temp.showCelsius
          },
          RigidArea {
          width: 20
          },
          CheckBox {
          text: "Show Farenheit"
          selected: bind temp.showFarenheit
          }]
          },
          Slider {
          visible: bind temp.showCelsius
          min: -100
          max: 100
          border: TitledBorder {title: "Celsius"}
          value: bind temp.celsius
          minorTickSpacing: 5
          majorTickSpacing: 10
          paintTicks: true
          paintLabels: true
          labels:
          [SliderLabel {
          value: 0
          label: SimpleLabel {
          text: "0"
          }
          },
          SliderLabel {
          value: 100
          label: SimpleLabel {
          text: "100"
          }
          }]
          }
          ,
          Slider {
          visible: bind temp.showFarenheit
          border: TitledBorder {title: "Farenheit"}
          min: -148
          max: 212
          paintTicks: true
          minorTickSpacing: 5
          majorTickSpacing: 10
          value: bind temp.farenheit
          paintLabels: true
          labels:
          [SliderLabel {
          value: 0
          label: SimpleLabel {
          text: "0"
          }
          },
          SliderLabel {
          value: 32
          label: SimpleLabel {
          text: "32"
          }
          },
          SliderLabel {
          value: 212
          label: SimpleLabel {
          text: "212"
          }
          }]
          }
          ,
          FlowPanel {
          alignment: LEADING
          content:
          [SimpleLabel {
          visible: bind temp.showCelsius
          alignmentX: 1
          text: "Celsius:"
          },
          Spinner {
          visible: bind temp.showCelsius
          min: -100
          max: 100
          value: bind temp.celsius
          }
          ,
          RigidArea {
          width: 20
          },
          SimpleLabel {
          visible: bind temp.showFarenheit
          alignmentX: 1
          text: "Farenheit:"
          },
          Spinner {
          visible: bind temp.showFarenheit
          min: -148
          max: 212
          value: bind temp.farenheit
          }
          ]
          }]
          }

          visible: true

          }

          示例代碼中與Spinner和Slider相關(guān)的部分以粗體表示。Spinner和Slider都具有minmax屬性,這些屬性決定了它們的取值范圍,而value屬性則是其當前取值。

          在上面示例采用攝氏溫度的Spinner和Slider中,它們的value屬性綁定為模型的celsius屬性。而在采用華氏溫度的Spinner和Slider中,value屬性綁定為模型的farenheit屬性。并且在模型的celsiusfarenheit屬性上定義了觸發(fā)器,無論這兩個屬性值中哪個發(fā)生變化,都將相應(yīng)地更新另一個屬性。因此,無論移動slider或者修改spinner,相關(guān)的數(shù)據(jù)都將發(fā)生變化。

          例如,如果我們將溫度設(shè)置為88華氏度:

          Slider還具有一些決定如何顯示浮標行的屬性。另外,通過將一組SliderLabel賦值給Slider的labels,我們可以為特定的數(shù)值加標簽。在本例中,冰點(freezing)和沸點(boiling)、0華氏度就是這樣做的。

          top

          相關(guān)資源

          top

          關(guān)于譯者

          cleverpig:BJUG成員,Java社區(qū)——Matrix與Java共舞負責人之一,曾參與Buffalo的文檔工作、Fielding的《Architectural Styles and the Design of Network-based Software Architectures》中文化研究(還要感謝Tin、Nicholas的大力相助),關(guān)注一切新技術(shù),業(yè)余時間研究Guru并準備得道升天,但是苦于沒有得法,目前還在苦苦追尋……

          Tin:中文名“田樂”,BJUG成員,現(xiàn)就職于Sina。曾經(jīng)在Java Web項目中擔任軟件架構(gòu)師和Web設(shè)計,注重使用輕量級解決方案和敏捷方法。目前主要做基于Javascript的RIA開發(fā),喜歡研究新技術(shù)并進行思考,業(yè)余時間繼續(xù)關(guān)注Java和Ruby,并與朋友一起翻譯Selenium文檔

          top



          凡是有該標志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
          、轉(zhuǎn)載請注明來處和原文作者。非常感謝。

          posted on 2007-08-17 00:36 草兒 閱讀(2683) 評論(1)  編輯  收藏 所屬分類: javaJAVA WEB應(yīng)用

          Feedback

          # re: 深入學習JavaFX腳本語言-參考 2008-09-06 22:36 日月雨林@gmail.com
          相當?shù)牟诲e,最近在看JavaFX呢!  回復  更多評論
            

          主站蜘蛛池模板: 洪江市| 时尚| 滦南县| 噶尔县| 安图县| 关岭| 金阳县| 南汇区| 五指山市| 大英县| 达孜县| 拉萨市| 大埔区| 涞源县| 连云港市| 东港市| 疏勒县| 略阳县| 茶陵县| 广元市| 响水县| 盐山县| 潮安县| 枣庄市| 峨山| 信阳市| 察哈| 仁怀市| 东阳市| 遂宁市| 巴林右旗| 阳新县| 科尔| 浦江县| 潍坊市| 炉霍县| 略阳县| 甘泉县| 定襄县| 静海县| 兴隆县|