關於本教程
本教程覆蓋了JavaFX API中內置的JavaFX UI控件。
本文包含以下章節:
- JavaFX的UI控件
- 標籤
- 按鈕
- 單選按鈕
- 切換按鈕
- 複選框
- 選擇框
- 文本框
- 密碼框
- 滾動條
- 滾動窗格
- 列表視圖
- 表格
- 分隔條
- 滑塊
- 進度條和進度指示器
- 超鏈接
- 工具提示
- HTML編輯器
- 標題面板和可摺疊面板
每章提供了代碼示例和應用程序來說明實際的UI控件的功能。你可以找到上表列出的應用程的源文件和對應的NetBeans項目文件。
JavaFX UI控件
通過JavaFX API提供的JavaFX UI控件通過場景中的結點來構建,因此,控件可以使用JavaFX平臺的豐富的視覺功能。由於JavaFX API是完全實現自Java的,因此你可以方便地將JavaFX UI控件集成到已有的Java應用程序中。
1 JavaFX2.0中支持的UI控件
構建UI的類存在於API的javafx.scene.control包中。圖1-1展示了使用包中一些控件構建的例子的截屏圖。
圖1-1 JavaFXUI的例子
該例子使用用以下的類:
- 標籤
- 按鈕
- 單選按鈕
- 切換按鈕
- 複選框
- 多選框
- 文本框
- 密碼框
- 列表框
- 滾動面板
- 進度條
- 進度指示器
- 超鏈接
這個UI列表包含了以往你在開發Java客戶端程序時可能用到的典型控件,無論如何,JavaFX2.2 SDK還包含了一些新的控件,比如選項面板和表格控件。
圖1-2展示了三個包含社交網絡列表的選項面板程序的截屏圖。列表可以上下收縮。
圖1-2 選項面板
要查看完整的UI控件的列表,請參閱API文檔。
UI控件類相比典型的用戶交互類提供了額外的變量和方法來直觀地實現及戶交互,你可以通過層疊樣式表(CSS)爲控件指定特殊的風格。對一些不常用的任務,你可能需要通過繼承控件類創建自定義的UI組件或通過皮膚接口爲控件定義新的外觀。
嘗試JavaFX產品中提供的設定程序來測試控件的功能、表現和風格。
功能和效果
因爲javafx.scene.control包中的控件都繼承自Node類,它們都可以使用場景渲染、動畫、變換和動畫過渡來宗合展示。
考慮一個創建按鈕的任務,它使用了反射,將它的透明度連續動態地從最大變到最小。
圖1-3通過動畫展示了按鈕的三種狀態,左邊的圖是一個透明度爲1.0的按鈕,中的的按鈕透明度爲0.8,右邊的透明度爲0.5.
圖1-3 動畫按鈕
通過使用JavaFX API,你可以使用很少的代碼來實現這些功能。
例一創建並開始了一個無限期時間線,關鍵幀的時間間隔600毫秒,按鈕的透明度從默認值(1.0)逐漸變爲0.0。setAutoReverse方法可以反向變化。
例1:創建動畫按鈕
import javafx.animation.KeyFrame;
- import javafx.animation.KeyValue;
- import javafx.animation.Timeline;
- import javafx.util.Duration;
- import javafx.scene.control.Button;
- import javafx.scene.text.Font;
- import javafx.scene.effect.Reflection;
- Button button = new Button();
- button.setText("OK");
- button.setFont(newFont("Tahoma", 24));
- button.setEffect(newReflection());
- final Timeline timeline = new Timeline();
- //將循環次數設爲永久
- timeline.setCycleCount(Timeline.INDEFINITE);
- timeline.setAutoReverse(true);
- final KeyValue kv = newKeyValue(button.opacityProperty(), 0);
- final KeyFrame kf = newKeyFrame(Duration.valueOf(600), kv);
- timeline.getKeyFrames().add(kf);
- timeline.play();
你還可以在上例中使用javafx.scene.effect包中的其他可視效果,如陰影、光照或者動態模糊。
用CSS設定UI控件的風格
你可以通過層疊樣式表(CSS)定製內置的UI控件的外觀,在JavaFX中使用CSS與在HTML中基本相同,因爲都使用相同的CSS規範。控件的可視狀態通過例1-2的.css文件來定義。
例1-2:在CSS文件中定義UI控件的樣式
- /*controlStyle.css */
- .scene{
- -fx-font: 14pt "CambriaBold";
- -fx-color: #e79423;
- -fx-background: #67644e;
- }
- .button{
- -fx-text-fill: #006464;
- -fx-background-color: #e79423;
- -fx-border-radius: 20;
- -fx-background-radius: 20;
- -fx-padding: 5;
- }
你可以通過例1-3所示的方法,用Scene類中的getStylesheets方法在應用程序中開啓特定的樣式。
例1-3:應用CSS
- Scene scene = new Scene();
- scene.getStylesheets().add("/uicontrolssample/controlStyle.css");
因爲HTML中的每個元素有自已的ID和類屬性,因此JavaFX程序中的每個控件都可通過setId()和setStyleClass()方法獲得ID和樣式類。此外,你還可以直接在程序中定義控件的樣式。
例1-4中爲切換按鈕定義的-fx-base屬性覆蓋了在.css文件中爲場景中所有控件統一定義的相應屬性。
例1-4:在JavaFX程序中定義切換按鈕的樣式
- ToggleButton tb3 = new ToggleButton ("I don't know");
- tb3.setStyle("-fx-base: #ed1c24");
圖1-4展示了切換按鈕加入到程序中時的樣式。
圖1-4 將CSS樣式應用到切換按鈕
圖表
作爲對典型UI界面元素的補充,JavaFXSDK在javafx.scene.chart包中提供了現成的圖表控件。該控件支持的圖表種類爲:面積圖、柱狀圖、氣泡圖、折線圖、餅圖和散點圖。圖表可包含多個系列。
圖1-5展示了進口水果的餅圖
圖1-5 餅圖
不同於其他的Java客戶端工具包。使用JavaFXSDK,你可以在你的程序中僅用很少的代碼就可以創建這樣的圖表。例1-5展示了這種特性。
例1-5:創建圖表
- ObservableList<PieChart.Data> pieChartData =
- FXCollections.observableArrayList(
- newPieChart.Data("Grapefruit", 13),
- newPieChart.Data("Oranges", 25),
- newPieChart.Data("Plums", 10),
- newPieChart.Data("Pears", 22),
- newPieChart.Data("Apples", 30)
- );
- PieChart chart = new PieChart(pieChartData);
- chart.setTitle("Imported Fruits");
以上的代碼段包含以容:
- 通過PieChart.Data類生成數據
- 圖表通過PieChart類的實例來生成
- 創建的圖表包含了標題
將JavaFX2.0控件集成到Swing中
你可以將JavaFX2.0控件集成到現有的使用Swing工具包創建的Java客戶端應用程序中。
要將JavaFX內容集成到Swing程序中,其步驟爲:
1、 將使用到的所有JavaFXUI控件逐一添加javafx.scene.Scene對象中,使用容器或組來管理它們。
2、 將Scene對象添加到Swing程序的內容窗格中。
如果你需要將JavaFX的單一控件定位到已有的Swing程序中,你也必須完成上述的兩個步驟。
即使JavaFX控件被集成到了Swing程序中,JavaFX UI控件仍然使用Prism圖形庫來繪製,並且擁有該引擎所擁有的全站優勢。
關於JavaFX和Swing的集成,請參考“在Swing中使用JavaFX”教程來了解更多信息。
2 標籤控件
標籤控件位於JavaFX API的javafx.scene.control包中,它擴展了標籤類。使用標籤可以顯示文本元素。你可以將一段文本包裝在一個特定的空間內,還可以爲文本添加一個圖像。
圖2-1展示了三種普通的標籤用例,左邊的標籤是一個帶圖像的文本,中間的展示了旋轉後的文本,右邊的標籤繪製了一段文本。
圖2-1 標籤例子
創建標籤
JavaFX API提供了三種標籤類的構造器來創建標籤,如例2-1所示。
例2-1 創建標籤
- //空標籤
- Label label1 = new Label();
- //純文字標籤
- Label label2 = new Label("Search");
- //帶圖標和文字的標籤
- Image image = new Image(getClass().getResourceAsStream("labels.jpg"));
- Label label3 = new Label("Search", new ImageView(image));
一旦在代碼中創建了標籤,你可以通過標籤類的以下方法向標籤對象增加文字或圖像內容。
- setText(String text)方法—爲標籤對象指定文字內容
- setGraphic(Node graphic)—指定圖標
setTextFill方法指定顏色來繪製標籤的文本內容,研究一下例2-2,它首先創建了一個標籤,然後加了一個圖標、並指定了文本的繪製顏色。
例2-2 向標籤增加圖標和文本顏色
- Label label1 = new Label("Search");
- Image image = newImage(getClass().getResourceAsStream("labels.jpg"));
- label1.setGraphic(new ImageView(image));
- label1.setTextFill(Color.web("#0076a3"));
將這段代碼加到程序中後,將產生圖2-2所示的標籤效果。
圖2-2帶圖標的標籤
如果你同時爲標籤定義了文本和圖像內容,你可以通過setGraphicTextGap方法來指定文本與圖像間的間距。
另外,你還可以通過setVPos和setHPos方法來改變標籤在其佈局區內的位置,或者通過setTextAlignment方法來設定文本元素的對齊方式。你還可以通過setContentDisplay方法,再指定常量:LEFT,RIGHT,CENTER,TOP,BOTTPM來定義圖像相對於文本的位置。
設定字體
對比圖2-1與2-2中的搜索標籤,可以發現圖2-1中的標籤字體較大。這是因爲例2-2代碼段沒有爲標籤指定任何字體,它使用了默認的字體大小來繪製。
要爲標籤提供一個字體文字大小以替換默認大小,請使用Labeled類中的setFont。在代碼片段2-3中將label1的文本大小設爲30點,字體名稱爲Arial。對於label2,大小爲32點,字體名稱Cambria。
例2-3 應用字體設置
- //使用字體裁類的構造方法
- label1.setFont(new Font("Arial", 30));
- //使用字體類的方法
- label2.setFont(Font.font("Cambria", 32));
或者,你還可以通過層疊樣式表(CSS)爲標籤控件指定字體設定。研究一下例2-4就可明白如何爲label1和label2指定相同的字體設定。
例2-4 設定標籤樣式
- label1.setStyle("-fx-font: 30 arial");
- label2.setStyle("-fx-font: 32 cambria");
多行文本
當你創建一個標籤時,有時你必須將標籤放置在一個比繪製區小的空間裏,將文本打斷(換行),以便以佈局大小相適應,爲setWrapText方法指定true值就可實現這種需求。如例2-5所示:
例2-5 開啓文本換行
- Label label3 = new Label("A label that needs to be wrapped");
- label3.setWrapText(true);
當label3被加入到程序的內容中,就會像圖2-3所示的效果一樣被繪製。
圖2-3 換行標籤
由於標籤的佈局大小不僅被它的寬度所限制,也被高度所限制。當不可能繪製整個字串時,你可以指定它的表現形式。使用標籤類的setTextOverrun方法,並選擇有效的OverrunStyle類型來處理不能被正確地繪製的那些文本。請參閱API文檔來了解OverrunStyle類型的更多信息。
應用特效
雖然標籤屬靜態文本,無法被編輯。但你可以使用可視化效果或者變換。例2-6的代碼段將label2旋轉了270度,並將變換了縱向位置。
例2-6 旋轉標籤
- Label label2 = new Label ("Values");
- label2.setFont(new Font("Cambria", 32));
- label2.setRotate(270);
- label2.setTranslateY(50);
旋轉和位移都是JavaFX API中典型的變換方法。此外,你還可以設置一種效果,當鼠標停留在標籤上時會將標籤放大。
例2-7中的代碼段對label3使用了放大效果。當標籤的MOUSE_ENTERED事件被觸發時,比例因子1.5應用到了setScaleX和setScaleY方法,當用戶將鼠標從標籤上移開時,MOUSE_EXITED事件發生,比例因子重設爲1.0,標籤使用原來的大小來繪製。
例2-7 應用放大效果
- label3.setOnMouseEntered(new EventHandler<MouseEvent>() {
- @Override public voidhandle(MouseEvent e) {
- label3.setScaleX(1.5);
- label3.setScaleY(1.5);
- }
- });
- label3.setOnMouseExited(new EventHandler<MouseEvent>() {
- @Override public voidhandle(MouseEvent e) {
- label3.setScaleX(1);
- label3.setScaleY(1);
- }
- });
圖2-4展示了label3的兩種狀態
圖2-4 放大標籤
相關API
- Label
- Labeled
3 按鈕控件
通過JavaFX API提供的Button類的方法,開發一員可以在用戶按下一個按鈕時來處理相應的動作。Button類繼承自Label類,它可以顯示文本、圖像者兩者相結合。圖3-1展示了不同的效果。本章將學習如何創建這些不同類型的按鈕。
圖3-1 按鈕的種類
創建一個按鈕
你可以在JavaFX程序中通過例3-1展示的三種構造方法來創建按鈕控件。
例3-1 創建按鈕
- //空按鈕
- Button button1 = new Button();
- //使用特定本文爲標題的按鈕
- Button button2 = new Button("Accept");
- //使用標題和圖標的按鈕
- Image imageOk = newImage(getClass().getResourceAsStream("ok.png"));
- Button button3 = new Button("Accept", new ImageView(imageOk));
由於Button類繼承自Label類,你可以通過以下的方法來爲未指定圖標或標題的按鈕指定按鈕的內容。
- setText(String text)—爲按鈕指定標題
- setGraphic(Node graphic)—指定圖標
例3-2展示瞭如何創建一個只有圖標沒有標題的按鈕
- Image imageDecline = newImage(getClass().getResourceAsStream("not.png"));
- Button button5 = new Button();
- button5.setGraphic(new ImageView(imageDecline));
當按鈕被加入到程 序中,這段代碼產生的按鈕如圖3-2.
圖3-2 帶圖標的按鈕
在例3-2和圖3-2中,圖標是一個ImageView對象。然而,你可以使用其他的圖形對象,例如,可以使用javafx.scene.shape包中的形狀。當同時爲按鈕定義文本和圖形後,你可以使用setGraphicTextGap方法來設定兩者的間距。
Button類的默認皮膚區分了以下的可視化狀態。圖3-3展示了一個帶圖標的按鈕的默認狀態。
圖3-3 按鈕狀態
賦予動作
按鈕的主要功能是在單擊時產生一個動作,使用Button類的setOnAction方法可以定義當用戶單擊按鈕時將要完成的事情,例3-3展示了爲button2定義動作的代碼片段。
例3-3 爲按鈕定義動作
- button2.setOnAction(new EventHandler<ActionEvent>() {
- @Override public voidhandle(ActionEvent e) {
- label.setText("Accepted");
- }
- });
ActionEvent是一個被EventHandler處理的事件類型,EventHandler對象提供了handle方法來處理按鈕觸發的動作,例3-3展示瞭如何覆寫handle方法,以更當用戶按下button2時標籤文本可以設爲“Accepted”。
你可以使用Button類來根據需要設定很多事件—處理(event--handling)方法,以便引發特定的行爲或應用可視化特效。
應用特效
因爲Button類繼承自Node類,你可以使作javafx.scene.effect包中的任何特效來增強按鈕的可視化效果。在例3-4中,當button3觸發了onMouseEntered事件時,該按鈕使用了DropShadow(譯註:一種陰影效果)效果。
例3-4 應用DropShadow特效
- DropShadow shadow = new DropShadow();
- //當鼠標位於按鈕上時,增加陰影特效
- button3.addEventHandler(MouseEvent.MOUSE_ENTERED,
- newEventHandler<MouseEvent>() {
- @Override public voidhandle(MouseEvent e) {
- button3.setEffect(shadow);
- }
- });
- //鼠標離開時,去除特效
- button3.addEventHandler(MouseEvent.MOUSE_EXITED,
- newEventHandler<MouseEvent>() {
- @Override public voidhandle(MouseEvent e) {
- button3.setEffect(null);
- }
- });
圖3-4展示了當鼠標進入和離開按鈕時的狀態。
圖3-4 有陰影特效的按鈕
使用樣式
改善按鈕可視化效果的下一步是應用通過Skin類定義的CSS樣式,在JavaFX2.0中使用CSS與在HTML中類似,因爲兩者都基於同樣的規範。
你可以將樣式定義在獨立的CSS文件中,然後在應用程序中通過setStyleClass方法來開啓,此方法繼承自Node類,所有的UI控件都起可用。另外,你還可以直接通過setStyle方法直接爲一個控件定義樣式。例3-5和圖3-4演示了這種方法。
例3-5 樣式化一個按鈕
- button1.setStyle("-fx-font: 22 arial; -fx-base: #b6e7c9");
-fx-font-size屬性爲button1設定字大屬性,-fx-base屬性覆蓋了按鈕的默認顏色。結果是,button1的顏色是淡綠色的,並且字體稍大,效果如圖3-5所示:
圖3-5 用CSS樣式化的按鈕
相關API
- Button
- Labeled
4 單選按鈕
單選按鈕(RadioButton)類實現自切換按鈕(ToggleButton)類,一個單選按鈕要麼選中,要麼不選。一般來說,單選按鈕都捆綁成一組,一次只能選擇一個。這種行爲可以和切換按鈕區分開來,因爲一組內的所有切換按鈕均可以有未選取的狀態。
圖4-1展示了三個單選按鈕例子的屏幕截圖,例子中三個單選按鈕屬同一組。
圖4-1 單選按鈕例子
通過研究後面的單節來學習如何在你的應用程序中實現單選按鈕。
創建單選按鈕
在JavaFX API的Javafx.scene.control包中,單選按鈕類提供了兩種創建單選按鈕的構造方法,例4-1展示了兩個按鈕,無參數的構造方法用來創建rb1,rb1的文本標題通過使用setText方法來實現,rb2的文本標題是通過相應的構造方法來定義的。
例4-1 創建單選按鈕
- //一個標題爲空的按鈕
- RadioButton rb1 = new RadioButton();
- //設定標題
- rb1.setText("Home");
- //一個有特定標題的按鈕
- RadioButton rb2 = new RadioButton("Calendar");
你可以明確地通過setSelected方法將一個單選按鈕設定爲選中狀態,並將它的值設定爲true。如果你需要檢查一個單選按鈕是否處於選中狀態,可心使用isSelected方法。
由於單選按鈕類是Labeled類的擴展類,你不但可以爲它設定標題,還可以設定圖像。使用setGraphic方法可以設定圖像。例4-2演示瞭如何在應用程序中實現圖像單選按鈕。
例4-2 創建圖像化單選按鈕
- Image image = newImage(getClass().getResourceAsStream("ok.jpg"));
- RadioButton rb = new RadioButton("Agree");
- rb.setGraphic(new ImageView(image));
向組中添加單選按鈕
典型的單選按鈕使用於組中,代表幾個相互獨立的選項。切換按鈕類爲所有單選按鈕提供的參考將它們聯繫在一起,以便在某一時刻只能選擇其中之一。例4-3創建了一個捆綁按鈕組,然後創建了三個單選按鈕,再把它們添加到組中,最後確定當程序啓動後哪一個按鈕被選中。
例4-3 創建一個單選按鈕組
- final ToggleGroup group = new ToggleGroup();
- RadioButton rb1 = new RadioButton("Home");
- rb1.setToggleGroup(group);
- rb1.setSelected(true);
- RadioButton rb2 = new RadioButton("Calendar");
- rb2.setToggleGroup(group);
- RadioButton rb3 = new RadioButton("Contacts");
- rb3.setToggleGroup(group);
當這些按鈕通過佈局容器放置並添加到程序中後,輸出如圖4-2所示。
圖4-2 綁定在一組中的三個單選按鈕
處理單選按鈕的事件
典型地,當組中的單選按鈕被選中時,程序會執行一個動作。查看代碼段4-4可以學習如何根據單選按鈕的選取狀態來改變圖標。
例4-4 處理單選按鈕的動作
- ImageView image = new ImageView();
- rb1.setUserData("Home")
- rb2.setUserData("Calendar");
- rb3.setUserData("Contacts");
- final ToggleGroup group = new ToggleGroup();
- group.selectedToggleProperty().addListener(new ChangeListener<Toggle>(){
- public voidchanged(ObservableValue<? extends Toggle> ov,
- Toggle old_toggle, Togglenew_toggle) {
- if(group.getSelectedToggle() != null) {
- final Image image =new Image(
- getClass().getResourceAsStream(
- group.getSelectedToggle().getUserData().toString() +
- ".jpg"
- )
- );
- icon.setImage(image);
- }
- }
- });
例如,當rb3被選中時,getSelectedToogle方法返回“rb3”,getUserData方法返回“Contacts”。這樣,getResourceAsStream方法接收值“Contacts.jpg”。程序的輸出如圖4-1年示。
爲單選按鈕申請焦點
在一組單選按鈕中,默認第一個按鈕擁有焦點,此時如果你通過setSelected方法將組中的第二個按鈕設爲選中狀態,你將獲得如圖4-3所示的效果。
圖4-3 默認焦點設置
第二個按鈕被選中,但第一個焦點仍保留焦點。使用requestFocus函數可以改變焦點,如例4-5所示。
- rb2.setSelected(true);
- rb2.requestFocus();
當此函數被調用後,輸出結果如圖4-4所示。
圖4-4 爲所選按鈕設定焦點
相關API
- RadioButton
- Labeled
5 切換按鈕
切換按鈕類在JavaFX API中代表另個一種類型的按鈕,兩個或多個這種類型的按鈕可以被捆綁在一組中,但同一時刻只能選擇其中之一,或者都不選。圖5-1是一個在組中捆綁了三個切換按鈕的應用程序的截屏圖。程序根據選中的切換按鈕來確定顏色繪製一個矩形。
圖5-1 三個切換按鈕
創建一個切換按鈕
你可以在應用程序中使用切換按鈕類的三個構造函數中的任何一個來創建切換按鈕,如例5-1所示。
例5-1 創建切換按鈕
- //無標題和圖標的切換按鈕
- ToggleButton tb1 = new ToggleButton();
- //有標題的切換按鈕
- ToggleButton tb2 = new ToggleButton("Press me");
- //有標題和圖標的切換按鈕
- Image image = newImage(getClass().getResourceAsStream("icon.png"));
- ToggleButton tb3 = new ToggleButton ("Press me", newImageView(image));
一旦你在代碼中定義了切換按鈕,你可以將它們綁定爲一組,並設定一個特定的行爲。
向組中添加切換按鈕
實現切換按鈕類與單選按鈕類非常相似。然而,不同於單選按鈕,組中的切換按鈕不能強制選擇組中的單一按鈕。也就是說,當單擊選中的切換按鈕時會導致它變爲非選中狀態,但單擊一個選中的單選按鈕時則不會改變什麼。
花點時間來研究一個例5-2的代碼段。
例5-2 綁定組中的切換按鈕
- final ToggleGroup group = new ToggleGroup();
- ToggleButton tb1 = new ToggleButton("Minor");
- tb1.setToggleGroup(group);
- tb1.setSelected(true);
- ToggleButton tb2 = new ToggleButton("Major");
- tb2.setToggleGroup(group);
- tb2.setUserData(Color.LIGHTBLUE);
- ToggleButton tb3 = new ToggleButton("Critical");
- tb3.setToggleGroup(group);
圖5-2 組中的三個切換按鈕
一般來說,你用一組切換按鈕來爲每個按鈕指定特定的行爲。下一部份解釋如何利用切換按鈕來區分矩形的顏色。
設定行爲
切換按鈕類繼承自Node類的setUserData方法幫助你指定任何選項值,在例5-3中,用戶數據指示哪種顏色用來繪製矩形。
例5-3 爲切換按鈕設定用戶數據
- tb1.setUserData(Color.LIGHTGREEN);
- tb2.setUserData(Color.LIGHTBLUE);
- tb3.setUserData(Color.SALMON);
- final Rectangle rect = new Rectangle(167, 50);
- final ToggleGroup group = new ToggleGroup();
- group.selectedToggleProperty().addListener(newChangeListener<Toggle>(){
- public voidchanged(ObservableValue<? extends Toggle> ov,
- Toggle toggle, Togglenew_toggle) {
- if (new_toggle == null)
- rect.setFill(Color.WHITE);
- else
- rect.setFill(
- (Color)group.getSelectedToggle().getUserData()
- );
- }
- });
ChangeListener<Toggle>對象檢測組中的選擇項,如果組中無選擇項,則繪製一個白色的方框。如果選擇的組中的項目,則後續會調用getSelectedToggle和getUserData方法,根據返回的顏色來繪製方框。
例如,如果用戶選擇了tb2按鈕,getSelectedToggle.getUserData方法返回Color.LIGHTBLUE,結果如圖5-3所示。
圖5-3 使用選項按鈕來繪製方框
查看ToggleButtonSample.java可以檢查程序的完整代碼。
定製選項按鈕
你可以將CSS樣式應用到切換按鈕來改善這個應用程序,在JavaFX中,CSS的用法與在HTML中一樣,因爲兩者都基於同樣的規範。例5-4使用setStyle方法以改變切換按鈕的-fx-base樣式屬性。
例5-4 將樣式應用到切換按鈕
- tb1.setStyle("-fx-base: lightgreen");
- tb2.setStyle("-fx-base: lightblue");
- tb3.setStyle("-fx-base:salmon");
把這幾行代碼加到程序中後,產生的可視效果如圖5-4所示。
圖5-4 繪製切換按鈕
你可能還想試試其他的CSS樣式、或應用JAVAFXAPI中提供的動畫、變換各可視化特效。
相關API
- ToggleButton
- ToggleGroup
6 選擇框
CheckBox類提供了在應用程序中創建選擇框的能力。雖然選擇框看上去與單選按鈕類似,然而不能將它們綁定到一個切換組以具備同時選擇多個選項的能力(注:原文如此)。更多信息,請參考單選按鈕和切換按鈕章節。
圖6-1展示了一個程序的截圖。在這個程序裏,使用了三個選擇框來打開和關閉程序中工具條的圖標。
圖6-1 選擇框例子
創建選擇框
例6-1 創建2個簡單選擇框
- //第一個沒有標籤
- CheckBox cb1 = new CheckBox();
- //另一個有文字標籤
- CheckBox cb2 = new CheckBox("Second");
- cb1.setText("First");
- cb1.setSelected(true);
一旦創建了選擇框,你可以通過JAVAFXAPI中提供的方法來修改它。在例6-1中,setText方法定義了c1的標籤,setSelected方法用來將cb1的狀態設爲選中,以便在程序開始時使cb1這個選擇框選中。
定義狀態
選擇框可以已定義或未定義,當處於已定義狀態時,使用以選取或不選它。然而,當選擇框未定義時,它不能被選擇或不選擇。可使用CheckBox類的setSelected和setDefined兩個方法組給來設定選擇框的狀態。表6-1展示了根據它的DEFINED和SELECTED屬性來決定的選擇框狀態。
表6-1 選擇框的狀態
Property Values | Checkbox Appearance |
---|---|
DEFINED = true SELECTED = false |
|
DEFINED = true SELECTED = true |
|
DEFINED = false SELECTED = true/false |
設定行爲
當選擇框用來代表UI要素混合的三種狀態(例如“YES”、“NO”、“NOT”)時,你可能需要使應用程序的選擇框具備三種狀態。選擇框的“ALLOW_TRI_STATE”屬性決定了選擇框是否能在三種狀態(“選中”、“未選中”、“未定義”)中循環。如果該屬性爲true,則選擇框可在三種狀態中循環,否則控件只能在兩種狀態中循環。後續部份描述的程序構造了三個選擇框,並且只打開了兩種狀態。
例6-2的代碼片段創建了三個選擇框,因此如果一個選擇框被選中了,則相應的圖標會出現在工具條上。
例6-2 設定選擇框的行爲
- final String[] names = new String[]{"Security","Project", "Chart"};
- final Image[] images = new Image[names.length];
- final ImageView[] icons = new ImageView[names.length];
- final CheckBox[] cbs = new CheckBox[names.length];
- for (int i = 0; i < names.length; i++) {
- final Image image = images[i] =
- newImage(getClass().getResourceAsStream(names[i] + ".png"));
- final ImageView icon = icons[i] =new ImageView();
- final CheckBox cb = cbs[i] = newCheckBox(names[i]);
- cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
- public voidchanged(ObservableValue<? extends Boolean> ov,
- Boolean old_val, Booleannew_val) {
- icon.setImage(new_val? image : null);
- }
- });
- }
Names數組使用了一個for循環來創建選擇框數組及相應的圖標數組。例如,第一個選擇框cbs[0]被賦予了“Security”標籤。同時,當第一個圖標對應的圖像創建後,image[0]接收“Security.png”作爲一個文件名提供給getResourceStream方法。如果特定的選擇框被選中時,對應的圖像被賦予相應的圖標。如果選擇框未選中,圖標接收一個null值,相應的圖標不會被繪製。
圖6-2展示了“Security”和“Chart”選項框被選中、“Project”選項框未選中的程序的情況。
圖6-2 賦予動作的選擇框的程序
定製選項框
圖6-2中的選擇框設爲CheckBox類的默認外觀,你可以通過例6-3所示的方法來通過setStyle方法改變選擇框的外觀。
例6-3 定製選擇框
- cb1.setStyle(
- "-fx-border-color:lightblue; "
- + "-fx-font-size: 20;"
- + "-fx-border-insets: -5;"
- + "-fx-border-radius:5;"
- + "-fx-border-style:dotted;"
- + "-fx-border-width:2;"
- );
新的樣式包含了點狀的淺藍色邊框、增大的字體。圖6-3展示了應用這種樣式的選擇框cb1。
圖6-3 樣式化的選擇框
要爲所有選擇框設定特定的樣式,請使用下列的過程:
- 創建一個.css文件
- 在.css文件中創建選擇框的CSS類
- 在選擇框的CSS類中定義需要的樣式
- 在你的JavaFX程序中通過使用setStyleClass方法來應用樣式單
相關API
- CheckBox
- JavaFX CSS Specification
7 組合框
組合框提供了在幾個選項中快速選擇的能力,考慮一下圖7-1所示的組合框的實現。
例7-1 創建一個有三個選項的組合框
創建組合框
例7-1 創建一個有三個選項的組合框
- ChoiceBox cb = new ChoiceBox(FXCollections.observableArrayList(
- "First","Second", "Third")
- );
例7-1展示了一個在ChoiceBox類內部創建項目列表的例子。列表項通過使用一個觀察者數組來提供。當然,你可以使用類的空構造器來創建組合檔,然後通過setItems方法指定項目列表。如例7-2所示。
例7-2有文本元素和分隔條的組合框
- ChoiceBox cb = new ChoiceBox();
- cb.setItems(FXCollections.observableArrayList(
- "New Document","Open ",
- new Separator(),"Save", "Save as")
- );
注意:組合框不僅可包含文本,也可以包含其他對象。在例7-2中,使用一個分隔條來分開選項。當運行這個代碼段時,產生的效果如圖7-2所示。
圖7-2 通過組合框創建的菜單
在真實的應用中,組合框用來構建多個選項的列表。
設定組合框的行爲
圖7-3所示的應用程序提供了一個有5個選項的組合框,當一個選定特定的語言時,相應的歡迎語被繪製出來。
圖7-3 多選項列表
例7-4提供了一個代碼段來展現如何通過從組合框中選定項目來決定哪個歡迎語被繪製。
例7-4 選擇一個組合框項目
ChangeListener<Number>對象通過連續調用getSelectionModel和getSelectedIndex方法來檢測當前選取的項目的序號。getSelectionModel方法返回被選項目,getSelectedIndex方法返回組合框cb的SELECTED_INDEX屬性。結果是,作爲序號的數值定義了歡迎數組的一個元素,並且爲標籤指定了一個相應的文本。例如,如果用戶選擇第二個選項,它對應的是西班牙語,所選序號是1,從greetings數組中選出第一個值“Hola”作爲歡迎語。這樣,標籤繪製出“Hola”。
你可以通過使用提示來使組合框擁有更多的信息量。提示是javafx.scene.control包中提供的一個UI控件,它可以應用到任何JavaFX UI控件。
使用工具提示
Tooltip類提供了一個預定的提示語,它可以通過調用setTooltip方法來方便的爲組合框(或其他任何控件)提供提示語。如例7-5所示。
例7-5 爲組合框增加提示語
- cb.setTooltip(new Tooltip("Select the language"));
組合框cb應用了更改後的提示,當用戶將鼠標放在組合框上時,會看到圖7-5所示的內容。
圖7-5 應用工具提示的組合框
爲了進一步改善你的應用程序,你可以通過CSS屬性或應用可視化特效來定製組合框。
相關API
- ChoiceBox
- Tooltip
8 文本框
TextBox類實現了一種接受並顯示輸入的UI控件,它提供了接受用戶輸入文本的功能。與之相應的還有另一種輸入控件:PasswordBox,它繼承自TextInputControl類。所有文本控件的超類都通過JavaFX API來提供。
圖8-1展示了一個附標籤的典型文本框
圖8-1 標籤和文本框
創建文本框
在例8-1中,一個文本框與一個標籤組合起來,以便指出應該在文本框中填寫什麼內容。
例8-1 創建文本框
- Label label1 = new Label("Name:");
- TextBox textBox = new TextBox ();
- HBox hb = new HBox();
- hb.getChildren().addAll(label1, textBox);
- hb.setSpacing(10);
你可以像例8-1一樣的創建一個空的文本框,也可以創建一個有實際文字數據的文本框。要創建一個有特定內容的文本框,請使用TextBox類的這個構造器:TextBox(“Hello World”)。你可以在任何時侯通過調用getText方法來獲取文本框的值。
你可以使用TextInputControl類的setColumns方法來設定文本框的尺寸,定義它能接受的最大字符數。
用文本框來構建UI
一般地,TextBox對象在表單中來使用,以創建幾個文本框。圖8-2中的程序顯示了3個文本框,並且處理用戶的輸入。
圖8-2 文本框的例子
例8-2中的代碼段創建了三個文本框和兩個按鈕,然後通過使用GridPane容器將它們添加到程序的場景中。當你需要爲你的UI實現靈活的佈局時,使用容器可提供極大的便利。
例8-2 在程序中添加文本框
- //Creating a GridPane container
- GridPane grid = new GridPane();
- grid.setPadding(new Insets(10, 10, 10, 10));
- grid.setVgap(5);
- grid.setHgap(5);
- //Defining the Name text field
- final TextBox name = new TextBox();
- name.setPromptText("Enter your first name.");
- name.setColumns(18);
- GridPane.setConstraints(name, 0, 0);
- grid.getChildren().add(name);
- //Defining the Last Name text field
- final TextBox lastName = new TextBox();
- lastName.setPromptText("Enter your last name.");
- GridPane.setConstraints(lastName, 0, 1);
- grid.getChildren().add(lastName);
- //Defining the Comment text field
- final TextBox comment = new TextBox();
- comment.setColumns(24);
- comment.setPromptText("Enter your comment.");
- GridPane.setConstraints(comment, 0, 2);
- grid.getChildren().add(comment);
- //Defining the Submit button
- Button submit = new Button("Submit");
- GridPane.setConstraints(submit, 1, 0);
- grid.getChildren().add(submit);
- //Defining the Clear button
- Button clear = new Button("Clear");
- GridPane.setConstraints(clear, 1, 1);
- grid.getChildren().add(clear);
讓我們花點時間來學習這個代碼段。name、lastName、comment文本框通過TextBox類的空構造器來創建。與例8-1不同,標籤在代碼段中沒有附着於文本框,取而代之的是使用提示文字來通知用戶應該在文本框中輸入何種數據。setPromptText方法定義了程序開始時出現在文本框中的文字。當例8-2被添加到程序中時,會產生圖8-3所示的輸出。
圖8-3 具有提示的三個文本框
提示文本和用戶鍵入的內容有所不同,提示文本不能通過getText方法獲得。
在真實的應用程序中,輸入到文本框中的數據根據特定商業需求的程序邏輯來處理。下節將解釋如何使用文本框來評估輸入的文本,並向用戶產生一個迴應。
處理文本框數據
就像先前提到的一樣,用戶輸入到文本框中的數據可以通過TextInputControl類的getText方法來獲得。
研究8-3來學習如何處理文本框對象的數據。
例8-3 爲提交和清除按鈕定義動作
- //Adding a Label
- final Label label = new Label();
- GridPane.setConstraints(label, 0, 3);
- GridPane.setColumnSpan(label, 2);
- grid.getChildren().add(label);
- //Setting an action for the Submit button
- submit.setOnAction(new EventHandler<ActionEvent>() {
- @Override
- public void handle(ActionEvent e){
- if ((comment.getText() !=null && !comment.getText().isEmpty())) {
- label.setText(name.getText() + " " + lastName.getText() +", "
- + "thank you foryour comment!");
- } else {
- label.setText("Youhave not left a comment.");
- }
- }
- });
- //Setting an action for the Clear button
- clear.setOnAction(new EventHandler<ActionEvent>() {
- @Override
- public void handle(ActionEvent e){
- name.clear();
- lastName.clear();
- comment.clear();
- label.setText(null);
- }
- });
添加到GridPane中的標籤控件向用戶繪製程序的迴應。當用戶按下提交按鈕時,setOnAction方法檢查文本框的內容。如果是非容字符,一個感謝信息被繪製。否則,程序通知用戶內容還設有確定,如圖8-4所示。
圖8-4 文本框爲空
當用戶按下清除按鈕時,清除方法被調用,刪除所有三個文本框中的內容。
學習一下你可能在使用文本框時會用到的其他有用的方法。
- copy()–將當前選取的文本傳遞到剪貼板上,保留所選內容.
- cut()–將當前選取的文本傳遞到剪貼板上,清除所選內容.
- paste()–將剪貼板中的內容傳遞到文本框中,替換所選部份.
相關API
- TextBox
- TextInput
9 密碼框
密碼框實現了一個特別的文本輸入控件,用戶鍵入的字符被隱藏,取而代之的是顯示一個迴應字串。圖9-1展示了一個有提示語的密碼框。
圖9-1 有提示的密碼框
創建密碼框
先用例9-1的代碼來創建一個初級的密碼框。
例9-1 創建密碼框
- PasswordBox passwordBox = new PasswordBox();
- passwordBox.setColumns(12);
- passwordBox.setPromptText("Your password");
在你的用戶界面上,可以爲密碼框指定一個伴隨的提示語或增加一個通知標籤。和TextBox類一樣,PasswordBox提供了setText方法來在控件中繪製文本。然而,用setText方法設定的字串在密碼框中會被迴應字符掩蓋。默認的迴應字符是“*”號。圖9-2展示了有預定義字符的密碼框。
圖9-2 設定了文字的密碼框
在密碼框中鍵入的值可以通過getText方法獲得,你可以在你的程序中處理這個值來設定適當的驗證邏輯。
設定密碼
花點時間看下例9-2的代碼,它演示了在你的用戶界面中實現的密碼框。
例9-2 實現驗證邏輯
- final Label message = new Label("");
- VBox vb = new VBox();
- vb.setPadding(new Insets(10, 0, 0, 10));
- vb.setSpacing(10);
- HBox hb = new HBox();
- hb.setSpacing(10);
- hb.setAlignment(Pos.CENTER_LEFT);
- Label label = new Label("Password");
- final PasswordBox pb = new PasswordBox();
- pb.setColumns(12);
- pb.setAction(new Runnable() {
- @Override
- public void run() {
- if(!pb.getText().equals("T2f$Ay!")) {
- message.setText("Your password is incorrect!");
- message.setTextFill(Color.rgb(210, 39, 30));
- } else {
- message.setText("Your password has been confirmed.");
- message.setTextFill(Color.rgb(21, 117, 84));
- }
- pb.clear();
- }
- });
- hb.getChildren().addAll(label, pb);
- vb.getChildren().addAll(hb, message);
密碼框的驗證邏輯在setAction實例變量中被定義,變理代表一個函數,當鍵入的值提交後,它就會被調用。一般而言,當按下回車鍵時就會被調用。如果鍵入的值與預設的不一致,相應的紅色提示信息會出現。如圖9-3.
圖9-3 密碼不正確
如果鍵入的值滿足預定標準,確認信息出現。如圖9-4.
因爲安全原因,當值被鍵入後,一般需要清除密碼框中的數據。在例9-2中,認證完成後調用了clear方法來清除。
設定迴應字符
你可以通過調用PasswordBox類的setEchoChar來更改默認迴應符。另一種方法是在定義控件的時侯在PasswordBox類的構造器中指定默認的迴應字符。例9-3表現了這兩種意圖。
例9-3 使用不同的迴應符
- //Setting an echo character within a constructor
- PasswordBox pb1 = new PasswordBox("#");
- //Setting an echo character by using the setEchoChar method
- PasswordBox pb2 = new PasswordBox();
- pb2.setEchoChar("{1}quot;);
另外,例9-4中的setHideDelay方法提供了一個機會來設定一個毫秒級的時間延遲,它可以在鍵入字符時延遲一定的時間後再顯示迴應符(注:很Cool的改進,我第一次在我的Android智能手機中體驗過)。這樣,用戶可以在繼續前確認鍵入的內容。
例9-4 爲迴應符設定延遲
- pb.setHideDelay(500);
例9-4爲密碼框設定一個500毫秒的延遲,當用戶在裏面鍵入時,他分辨率會體驗到圖圖9-5所示的感覺。
圖9-5 設定延遲後鍵入密碼
相關API
- PasswordBox
- TextInputControl
10 滾動條
ScrollBar類允許你在應用程序中使用滾動的面板和視圖。圖10-1展示了滾動條的三個區域:滑塊、左右(上下)按鈕和跟蹤器。
圖10-1 滾動條元素
創建滾動條
讓我們看一看例10-1的代碼片段。
例10-1 簡單的滾動條
- ScrollBar sc = new ScrollBar();
- sc.setMin(0);
- sc.setMax(100);
- sc.setValue(50);
setMin和setMax方法定義滾動條所代表的最小、最大值。當用戶滑動滑塊時,滾動條的值改變。在例10-1中,值是50。因此當程序開始時,滑塊位於滾動條的中間。滾動條默認是水平放置的。然而,你可以通過調用setOrientation方法將它設定爲垂直放置。
用戶可以單擊左右按鈕(在垂直模式是上下按鈕)來滾動一個單位。UNIT_INCREMENT屬性定義當用戶單擊時滾動條滾動的量。另一個方法是單擊跟蹤區來滑動多個單位值,BLOCK_INCREMENT屬性定義了單擊跟蹤條時滾動條的滾動量。
在你的程序中,你可以使用多種方法來滾動瀏覽超出可用區域的圖形元素。
在程序中使用滾動條
從動作來理解滾動條。例10-2的程序實現了一個滾動場景來瀏覽圖像。這個程序的任務是讓用戶瀏覽垂面板的內容,它比場景的高度大。
例10-2 滾動瀏覽多個圖像
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.geometry.Orientation;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.ScrollBar;
- import javafx.scene.effect.DropShadow;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.VBox;
- import javafx.scene.paint.Color;
- import javafx.stage.Stage;
- public class Main extends Application {
- final ScrollBar sc = newScrollBar();
- final Image[] images = newImage[5];
- final ImageView[] pics = newImageView[5];
- final VBox vb = new VBox();
- DropShadow shadow = newDropShadow();
- @Override
- public void start(Stage stage) {
- Group root = new Group();
- Scene scene = new Scene(root, 180, 180);
- scene.setFill(Color.BLACK);
- stage.setScene(scene);
- stage.setTitle("Scrollbar");
- root.getChildren().addAll(vb,sc);
- shadow.setColor(Color.GREY);
- shadow.setOffsetX(2);
- shadow.setOffsetY(2);
- vb.setLayoutX(5);
- vb.setSpacing(10);
- sc.setLayoutX(scene.getWidth()-sc.getWidth());
- sc.setMin(0);
- sc.setOrientation(Orientation.VERTICAL);
- sc.setPrefHeight(180);
- sc.setMax(360);
- for (int i = 0; i < 5;i++) {
- final Image image =images[i] =
- newImage(getClass().getResourceAsStream(
- "fw"+(i+1)+ ".jpg")
- );
- final ImageView pic =pics[i] =
- newImageView(images[i]);
- pic.setEffect(shadow);
- vb.getChildren().add(pics[i]);
- }
- sc.valueProperty().addListener(new ChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val,Number new_val) {
- vb.setLayoutY(-new_val.doubleValue());
- }
- });
- stage.setVisible(true);
- }
- public static void main(String[]args) {
- Application.launch(args);
- }
- }
程序的前幾行向場景中添加了一個內含圖像和滾動條的垂直面板。
當滾動條的Value屬性改變時,垂直面板的Y座標一起改變。因此當滑塊移動、單擊按鈕或跟蹤塊時,垂直面板移動。如例10-3。
例10-3 垂直滾動條的實現
- sc.valueProperty().addListener(new ChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val, Numbernew_val) {
- vb.setLayoutY(-new_val.doubleValue());
- }
- });
編譯運行程序,輸出如圖10-2所示。
圖10-2 滾動條例子
本程序演示了滾動條的典型應用,你也可以定製這個類在場景中創建滾動條。與其他任何UI控件和結點一樣,滾動條可以通過樣式來改變默認的實現風格。
樣式化滾動條
考慮一下通過層疊樣式表來樣式化你的滾動條。例10-4演示瞭如何直接通過代碼來樣式化滾動條。
例10-4 將CSS樣式應用到滾動條
sc.setStyle(
- "-fx-base:lemonchiffon;"
- + "-fx-border-color:darkgreen; "
- +"-fx-border-insets: -5; "
- +"-fx-border-radius: 10;"
- + "-fx-border-style:dotted;"
- + "-fx-border-width:2;"
- +"-fx-background-color: #b6e7c9;"
- +"-fx-background-radius: 10;"
- );
當把這些樣式應用到例10-1中的代碼中時,它的外觀改變了。請參見圖10-3來觀察結果。
圖10-3 樣式化的滾動條
你可以通過在程序中增加css文件,然後在程序中聲明相應的類來樣式化。
相關API
- ScrollBar
- JavaFX CSS規範
11 滾動面板
滾動面板爲UI要素提供滾動視圖。此控件使得用戶可以通過平移視圖或使用滾動條來滾動組件內容。默認設置的並附加了圖像的滾動面板如圖11-1所示。
圖11-1 滾動面板
創建滾動面板
例11-1演示瞭如何創建滾動面板。
例11-1 使用滾動面板來瀏覽圖像
- Image roses = newImage(getClass().getResourceAsStream("roses.jpg"));
- ScrollPane sp = new ScrollPane();
- sp.setNode(new ImageView(roses));
setNode方法定義了作爲滾動面板內容使用有結點。你可以僅指定一個結點,要創建有多個組件的滾動視圖,可以使用佈局容器或Group類。你也可以爲setPannable方法指定true值來通過單擊並移動鼠標瀏覽圖像,滾動條會作出相應的改變。
爲滾動面板設定滾動條策略
ScrollPane類提供了一種策略來決定何時出現滾動條:總是可見、隱藏或需要時出現。使用setHbarPolicy和setVbarPolicy方法來分別決定垂直或水平滾動條的滾動條策略。因此,在例11-2中,會出現垂直滾動條而沒有水平滾動條。
例11-2 設定水平及垂直滾動條策略
- sp.setHbarPolicy(ScrollBarPolicy.NEVER);
- sp.setVbarPolicy(ScrollBarPolicy.ALWAYS);
結果是,你只能垂直滾動圖像,如圖11-2所示。
圖11-2 關閉水平滾動條
在滾動面板中重設組件尺寸
設計UI界面的時候,你可能需要去調整組件的尺寸,以更能匹配滾動條的高度和寬度,將setFitToWidth或setFitToHeight方法的參數設爲true以匹配實際的尺寸。
圖11-3所示的滾動面板包含了單選按鈕、文本框和密碼框。組件的尺寸超出了滾動面板的預定尺寸,因此滾動條會自動出現。然而,由於將setFitToWidth的參數設爲了true,組件在水平位置上自動收縮,所以水平滾動條不會出現。
圖11-3 匹配滾動面板的寬度
默認地,FIT_TO_WIDTH和FIT_TO_HEIGHT的屬性均爲false,這樣可調整大小的組件保持原來的尺寸。如果你在此程序中將setFitToWidth方法刪除,你將看到圖11-4的結果。
圖11-4 匹配組件的默認屬性
這個滾動面板能讓你在水平、垂直方向檢索麪板內容當前、最小、最大值,你可以在你的程序中學習使用它。
附滾動面板的示例程序
例11-3使用滾動面板來顯示內附圖像的垂直框,ScrollPane類的VALUE屬性幫助你識別當前顯示的圖像,並繪製圖像的文件名。
例11-3 使用滾動面板顯示圖像
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.scene.Scene;
- import javafx.scene.control.Label;
- import javafx.scene.control.ScrollPane;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.Priority;
- import javafx.scene.layout.VBox;
- import javafx.stage.Stage;
- public class Main extends Application {
- final ScrollPane sp = newScrollPane();
- final Image[] images = newImage[5];
- final ImageView[] pics = newImageView[5];
- final VBox vb = new VBox();
- final Label fileName = newLabel();
- final String [] imageNames = newString [] {"fw1.jpg", "fw2.jpg",
- "fw3.jpg","fw4.jpg", "fw5.jpg"};
- @Override
- public void start(Stage stage) {
- VBox box = new VBox();
- Scene scene = new Scene(box,180, 180);
- stage.setScene(scene);
- stage.setTitle("ScrollPane");
- box.getChildren().addAll(sp,fileName);
- box.setVgrow(sp,Priority.ALWAYS);
- fileName.setLayoutX(30);
- fileName.setLayoutY(160);
- for (int i = 0; i < 5;i++) {
- final Image image =images[i] =
- new Image(getClass().getResourceAsStream(imageNames[i]));
- final ImageView pic =pics[i] =
- newImageView(images[i]);
- pics[i].setFitWidth(100);
- pics[i].setPreserveRatio(true);
- vb.getChildren().add(pics[i]);
- }
- sp.setVmax(440);
- sp.setPrefSize(115, 150);
- sp.setNode(vb);
- sp.vvalueProperty().addListener(new ChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val,Number new_val) {
- fileName.setText(imageNames[(new_val.intValue() - 1) / 100]);
- }
- });
- stage.setVisible(true);
- }
- public static void main(String[]args) {
- Application.launch(args);
- }
- }
編譯和運行這段代碼產生圖11-5的輸出。
圖11-5 滾動圖像
滾動條的最大值與垂直框的高度相等,如下例11-4的代碼段繪製已顯示圖像的文件名稱。
例11-4 跟蹤滾動面板的垂直移動
- sp.vvalueProperty().addListener(new ChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val, Numbernew_val) {
- fileName.setText(imageNames[(new_val.intValue() - 1) / 100]);
- }
- });
ImageView對象限制了圖像的高度爲100像素,這樣,new_val_intValue-1除以100後,結果返回在imageNames數組中的當前圖像。
在實際使用時,你還可以改變水平和垂直滾動條的最大最小值,這樣可以動態更新你的用戶界面。
相關API
- ScrollPane
- ScrollBar
12 列表視圖
列表視圖類代表一個可滾動項目的列表,圖12-1展示了所有可選的客房類型。
圖12-1 簡單列表視圖
你可以通過使用setItems方法定義項目的方式來產生列表,你還可以應用setCellFactory方法來創建列表項的視圖。
創建列表視圖
例12-1的代碼段實現了用字符列表項實現了圖12-1的功能。
例12-1 創建列表視圖
- ListView<String> list = new ListView<String>();
- ObservableList<String> items =FXCollections.observableArrayList (
- "Single","Double", "Suite", "Family App");
- list.setItems(items);
要改變列表控件的尺寸,可使用setPrefHeight和setPrefWidth方法。例12-2將列表大小改爲100×70,結果如圖12-2所示。
例12-2 設置列表視圖的高和寬
- list.setPrefWidth(100);
- list.setPrefHeight(70);
圖12-2 調整垂直列表大小
你可以將列表視圖的定位屬性設爲Orientation.HORIZONTAL來將它的朝向更改爲水平方向。可以這樣做:list.setOrientation(Orientation.HORIZONTAL)。包含和圖12-1所示的相同列表項的水平列表如圖12-3所示。
圖12-3 水平列表控制
你可以通過以下方法的組合來獲取列表視圖中每個列表項的狀態。
- getSelectionModel().selectedIndexProperty()—返回當前選中項的序號
- getSelectionModel().selectedItemProperty()—返回當前選中的列表項
- getFocusModel().getFocusIndex()—返加當前有焦點的列表項的序號
- getFocusModel().getFocusItem()—返加當前有焦點的列表項
需要注意的是,選取和聚焦狀態是隻讀的,程序開始後你不能指定哪一個項目被選中和聚焦。
前述的列子代碼展示瞭如何創建一個文字列表項,然而,列表視圖可以包含任何結點對象。
產生帶數據的列表視圖
研究下列代碼來學習如何使用單元格工廠方法產生列表項。例12-3的所示的程序創建一個顏色列表。
例12-3 創建單元格工廠
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.collections.FXCollections;
- import javafx.collections.ObservableList;
- import javafx.scene.Scene;
- import javafx.scene.control.Label;
- import javafx.scene.control.ListCell;
- import javafx.scene.control.ListView;
- import javafx.scene.layout.Priority;
- import javafx.scene.layout.VBox;
- import javafx.scene.paint.Color;
- import javafx.scene.shape.Rectangle;
- import javafx.scene.text.Font;
- import javafx.stage.Stage;
- import javafx.util.Callback;
- public class Main extends Application {
- ListView<String> list = newListView<String>();
- ObservableList<String> data= FXCollections.observableArrayList(
- "chocolate","salmon", "gold", "coral",
- "darkorchid","darkgoldenrod", "lightsalmon",
- "black","rosybrown", "blue", "blueviolet",
- "brown"
- );
- final Label label = new Label();
- @Override
- public void start(Stage stage) {
- VBox box = new VBox();
- Scene scene = new Scene(box,200, 200);
- stage.setScene(scene);
- stage.setTitle("ListViewSample");
- box.getChildren().addAll(list, label);
- box.setVgrow(list,Priority.ALWAYS);
- label.setLayoutX(10);
- label.setLayoutY(115);
- label.setFont(newFont("Verdana", 20));
- list.setItems(data);
- list.setCellFactory(newCallback<ListView<String>,
- ListCell<String>>() {
- publicListCell<String> call(ListView<String> p) {
- final Rectanglerect = new Rectangle(100, 20);
- finalListCell<String> cell = new ListCell<String>() {
- @Override publicvoid updateItem(String item,
- booleanempty) {
- super.updateItem(item, empty);
- if (item!= null) {
- rect.setFill(Color.web(item));
- setNode(rect);
- }
- }
- };
- return cell;
- }
- });
- list.setPrefHeight(100);
- stage.setVisible(true);
- }
- public static void main(String[]args) {
- Application.launch(args);
- }
- }
編譯運行這個程序,產生的輸出如下:
圖12-4 顏色列表
你可以滾動列表,選擇或取消列表中的任何項。你還可以擴充這個程序,使用顏色來填充文字標籤。
處理列表項的選取
增加例12-4所示的代碼段,可以實際項目被選取時產生產生事件。
例12-4 處理列表項的事件
- final Label label = new Label();
- list.getSelectionModel().selectedItemProperty().addListener(
- newChangeListener<String>() {
- public void changed(ObservableValue<? extends String> ov,
- String old_val, Stringnew_val) {
- label.setText(new_val);
- label.setTextFill(Color.web(new_val));
- }
- });
圖12-5 選擇深紫色的情況
相關API
- ListView
- ListCellFactory
13 表格視圖
JavaFX SDK中設計了幾個用於表現表格化數據的類,在JavaFX中用來創建表格的最重要的類是TableView、TableColumn和TableCell類。你可以通過實現數據模型或應用單元格生成器來創建表格。
表格類提供了內建的功能在需要時按列來排序或調整列的寬度。
圖13-1展示了一個地址簿中的聯繫人的典型表格。
圖13-1 表格例子
創建表格
例13-1所示的代碼段展示了創建三個列的表格,並將它增加到場景中的情況。
例13-1 增加一個表格
- import javafx.application.Application;
- import javafx.geometry.Insets;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Label;
- import javafx.scene.control.TableColumn;
- import javafx.scene.control.TableView;
- import javafx.scene.layout.VBox;
- import javafx.scene.text.Font;
- import javafx.stage.Stage;
- public class Main extendsApplication {
- private TableView table = newTableView();
- public static void main(String[]args) {
- launch(args);
- }
- @Override
- public void start(Stage stage) {
- Scene scene = new Scene(newGroup());
- stage.setTitle("TableView Sample");
- stage.setWidth(400);
- stage.setHeight(450);
- final Label label = newLabel("Address Book");
- label.setFont(new Font("Arial",20));
- table.setStyle("-fx-base: #b6e7c9;");
- TableColumn firstNameCol =new TableColumn("First Name");
- TableColumn lastNameCol = newTableColumn("Last Name");
- TableColumn emailCol = newTableColumn("Email");
- table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
- final VBox vbox = new VBox();
- vbox.setSpacing(5);
- vbox.getChildren().addAll(label, table);
- vbox.setPadding(newInsets(10, 0, 0, 10));
- ((Group)scene.getRoot()).getChildren().addAll(vbox);
- stage.setScene(scene);
- stage.setVisible(true);
- }
- }
表格控件通過實例化TableView類來創建。在例13-1中,表格由VBox容器來管理,然而,你可以將表格直接加入到應用程序的場景中。
例13-1定義了三個列來存儲地址簿中聯繫人的姓、名和郵件地址信息,這些列通過TableColumn類來創建。
TableView類的getColumns()方法將前面創建的列增加到表格中。在你的程序中,你可以通過該方法來動態地增加或刪除表格的列。
這個代碼段同時使用了setStyle()方法來改變默認的表格外觀。
編譯並運行此代碼,將產生如圖13-2的輸出。
圖13-2 沒有數據的表格
你可以通過調查setVisible方法來管理列是否可見。例如,如果你的應用程序的業務邏輯需要隱藏用戶的郵件列,可以這樣來實現:emailCol.setVisible(false)。
如果你的數據結構需要進行更復雜的表現方式,你可以創建一個嵌套列。
例如,假設地址簿中的聯繫人有兩個email地址。那麼就需要兩列來展現主要地址和次要地址。你可以通過創建兩個子列,再像例13-2所示的在emailCol列上調用getColumns方法來增加。
例13-2 創建嵌套列
- TableColumn firstEmailCol = new TableColumn("Primary");
- TableColumn secondEmailCol = new TableColumn("Secondary");
- emailCol.getColumns().addAll(firstEmailCol, secondEmailCol);
一旦你將這些代碼加到例13-1中後,然後編譯並運行程序代碼,就會產生圖13-3的樣式。
圖13-3 有嵌套列的表格
雖然表格加入到了應用程序中,但由於沒有數據,所以會出現一個標準表格標題“表格無數據”。如果要要改變這個標題,你可以調用setPlaceholder方法在空表中指定一個Node對象作爲佔位符。
定義表格模型
在程序中創建表格時,最好的方法是實現一個定義了數據模型,並提供了方法和屬性來操作表格的類。例13-3創建了Person類來定義地址簿中的數據。
例13-3 創建Person類
- private final StringProperty firstName;
- private final StringPropertylastName;
- private final StringPropertyemail;
- private Person(String fName,String lName, String email) {
- this.firstName = newStringProperty(fName);
- this.lastName = newStringProperty(lName);
- this.email = newStringProperty(email);
- }
- public String getFirstName() {
- return firstName.get();
- }
- public void setFirstName(StringfName) {
- firstName.set(fName);
- }
- public String getLastName() {
- return lastName.get();
- }
- public void setLastName(StringfName) {
- lastName.set(fName);
- }
- public String getEmail() {
- return email.get();
- }
- public void setEmail(StringfName) {
- email.set(fName);
- }
- }
當數據模型在Person類中被描述後,你可以創建一個可觀察列表(ObservableList)來定義你要在表中展示的任意多個數據行。例13-4的代碼段實現了這個任務。
例13-4 在ObservableList中定表表格數據
- final ObservableList<Person> data = FXCollections.observableArrayList(
- new Person("Jacob","Smith", "[email protected]"),
- new Person("Isabella","Johnson", "[email protected]"),
- new Person("Ethan","Williams", "[email protected]"),
- new Person("Emma","Jones", "[email protected]"),
- new Person("Michael","Brown", "[email protected]")
- );
下一步是將數據與表格的列關聯起來。你可以通過爲每個數據元素定義的屬性來完成。如例13-5。
例13-5 設置列的數據屬性
- firstNameCol.setProperty("firstName");
- lastNameCol.setProperty("lastName");
- emailCol.setProperty("email");
定義了數據模型後,數據被填入,並與列形成了關聯,你可以用TableView的setItems方法向表中增加數據。如:setItem(data)。
由於ObservableList對象可以跟蹤列表內元素的變化,因此當數據改變時TableView會自動更新其內容。
仔細閱讀一下例13-6的代碼。
例13-6 創建表格併爲表格添加數據
- import javafx.application.Application;
- import javafx.beans.property.StringProperty;
- import javafx.collections.FXCollections;
- import javafx.collections.ObservableList;
- import javafx.geometry.Insets;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Label;
- import javafx.scene.control.TableColumn;
- import javafx.scene.control.TableView;
- import javafx.scene.layout.VBox;
- import javafx.scene.text.Font;
- import javafx.stage.Stage;
- public class Main extends Application {
- public static class Person {
- private final StringPropertyfirstName;
- private final StringPropertylastName;
- private final StringPropertyemail;
- private Person(String fName,String lName, String email) {
- this.firstName = newStringProperty(fName);
- this.lastName = new StringProperty(lName);
- this.email = newStringProperty(email);
- }
- public String getFirstName(){
- return firstName.get();
- }
- public voidsetFirstName(String fName) {
- firstName.set(fName);
- }
- public String getLastName() {
- return lastName.get();
- }
- public voidsetLastName(String fName) {
- lastName.set(fName);
- }
- public String getEmail() {
- return email.get();
- }
- public void setEmail(StringfName) {
- email.set(fName);
- }
- }
- private TableView<Person>table = new TableView<Person>();
- private finalObservableList<Person> data =
- FXCollections.observableArrayList(
- newPerson("Jacob", "Smith","[email protected]"),
- newPerson("Isabella", "Johnson","[email protected]"),
- new Person("Ethan","Williams", "[email protected]"),
- newPerson("Emma", "Jones","[email protected]"),
- newPerson("Michael", "Brown","[email protected]")
- );
- public static void main(String[]args) {
- launch(args);
- }
- @Override
- public void start(Stage stage) {
- Scene scene = new Scene(newGroup());
- stage.setTitle("TableView Sample");
- stage.setWidth(400);
- stage.setHeight(450);
- final Label label = newLabel("Address Book");
- label.setFont(newFont("Arial", 20));
- table.setStyle("-fx-base: #b6e7c9;");
- TableColumn firstNameCol =new TableColumn("First Name");
- firstNameCol.setProperty("firstName");
- TableColumn lastNameCol = newTableColumn("Last Name");
- lastNameCol.setProperty("lastName");
- TableColumn emailCol = newTableColumn("Email");
- emailCol.setMinWidth(200);
- emailCol.setProperty("email");
- table.setItems(data);
- table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
- final VBox vbox = new VBox();
- vbox.setSpacing(5);
- vbox.getChildren().addAll(label, table);
- vbox.setPadding(newInsets(10, 0, 0, 10));
- ((Group)scene.getRoot()).getChildren().addAll(vbox);
- stage.setScene(scene);
- stage.setVisible(true);
- }
- }
當你編譯並運行這個程序後,產生的輸出如圖13-4所示:
圖13-4 裝填了數據的表格
增加新行
圖13-4的表格包含了5行數據,然而,目前爲止你還不能修改數據。
你可以使用文本框來鍵入姓、名、郵件地址的新值。文本框控件可以讓你的程序具有接受用戶輸入的能力。例13-7創建了三個文本框,併爲每個文本框定義了掩碼,然後創建了一個按鈕。
例13-7 使用文本框在表中鍵入新值
- final TextBox addFirstName = new TextBox();
- addFirstName.setPromptText("First Name");
- addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
- final TextBox addLastName = new TextBox();
- addLastName.setMaxWidth(lastNameCol.getPrefWidth());
- addLastName.setPromptText("Last Name");
- final TextBox addEmail = new TextBox();
- addEmail.setMinWidth(emailCol.getPrefWidth());
- addEmail.setPromptText("Email");
- final Button addButton = new Button("Add");
- addButton.setOnAction(new EventHandler<ActionEvent>() {
- @Override public voidhandle(ActionEvent e) {
- data.add(new Person(
- addFirstName.getText(),
- addLastName.getText(),
- addEmail.getText())
- );
- addFirstName.clear();
- addLastName.clear();
- addEmail.clear();
- }
- });
當用戶單擊增加按鈕時,鍵入文本框中的值被包含在Person類的構造器中增加到了可觀察列表中,這樣,新鍵入的聯繫人信息就會自動出現在表格中。
請仔細看一下例13-8的代碼。
例13-8 包含可鍵入新聯繫人的文本框的表格
- import javafx.application.Application;
- import javafx.beans.property.StringProperty;
- import javafx.collections.FXCollections;
- import javafx.collections.ObservableList;
- import javafx.event.ActionEvent;
- import javafx.event.EventHandler;
- import javafx.geometry.Insets;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Button;
- import javafx.scene.control.Label;
- import javafx.scene.control.TableColumn;
- import javafx.scene.control.TableView;
- import javafx.scene.control.TextBox;
- import javafx.scene.layout.HBox;
- import javafx.scene.layout.VBox;
- import javafx.scene.text.Font;
- import javafx.stage.Stage;
- public class Main extends Application {
- public static class Person {
- private final StringPropertyfirstName;
- private final StringPropertylastName;
- private final StringPropertyemail;
- private Person(String fName,String lName, String email) {
- this.firstName = newStringProperty(fName);
- this.lastName = newStringProperty(lName);
- this.email = newStringProperty(email);
- }
- public String getFirstName(){
- return firstName.get();
- }
- public voidsetFirstName(String fName) {
- firstName.set(fName);
- }
- public String getLastName() {
- return lastName.get();
- }
- public voidsetLastName(String fName) {
- lastName.set(fName);
- }
- public String getEmail() {
- return email.get();
- }
- public void setEmail(StringfName) {
- email.set(fName);
- }
- }
- private TableView<Person>table = new TableView<Person>();
- private finalObservableList<Person> data =
- FXCollections.observableArrayList(
- newPerson("Jacob", "Smith","[email protected]"),
- newPerson("Isabella", "Johnson","[email protected]"),
- newPerson("Ethan", "Williams","[email protected]"),
- new Person("Emma","Jones", "[email protected]"),
- newPerson("Michael", "Brown","[email protected]")
- );
- private HBox hb = new HBox();
- public static void main(String[]args) {
- launch(args);
- }
- @Override
- public void start(Stage stage) {
- Scene scene = new Scene(newGroup());
- stage.setTitle("TableView Sample");
- stage.setWidth(400);
- stage.setHeight(450);
- final Label label = newLabel("Address Book");
- label.setFont(newFont("Arial", 20));
- table.setStyle("-fx-base: #b6e7c9;");
- TableColumn firstNameCol =new TableColumn("First");
- firstNameCol.setProperty("firstName");
- TableColumn lastNameCol = newTableColumn("Last");
- lastNameCol.setProperty("lastName");
- TableColumn emailCol = newTableColumn("Email");
- emailCol.setMinWidth(200);
- emailCol.setProperty("email");
- table.setItems(data);
- table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
- final TextBox addFirstName =new TextBox();
- addFirstName.setPromptText("First Name");
- addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
- final TextBox addLastName =new TextBox();
- addLastName.setMaxWidth(lastNameCol.getPrefWidth());
- addLastName.setPromptText("Last Name");
- final TextBox addEmail = newTextBox();
- addEmail.setMinWidth(emailCol.getPrefWidth());
- addEmail.setPromptText("Email");
- final Button addButton = newButton("Add");
- addButton.setOnAction(newEventHandler<ActionEvent>() {
- @Override public voidhandle(ActionEvent e) {
- data.add(new Person(
- addFirstName.getText(),
- addLastName.getText(),
- addEmail.getText())
- );
- addFirstName.clear();
- addLastName.clear();
- addEmail.clear();
- }
- });
- hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton);
- hb.setSpacing(3);
- final VBox vbox = new VBox();
- vbox.setSpacing(5);
- vbox.getChildren().addAll(label,table, hb);
- vbox.setPadding(newInsets(10, 0, 0, 10));
- ((Group)scene.getRoot()).getChildren().addAll(vbox);
- stage.setScene(scene);
- stage.setVisible(true);
- }
- }
這個程序沒有提供任何過慮器來檢查數據,例如檢查郵件格式是否合法。開發你自已的程序時,可以提供這類功能。
現在這個版本也不檢查是否有空的輸入,如果沒有提供值,單擊按鈕會插入一個空行。
圖13-5演示了用戶如何增加一行數據。
圖13-5 向地址簿中增加聯繫人信息
圖13-6展示了單擊增加按鈕後的情況,Emma White的信息已經出現在表中了。
圖13-6 新增的條目
對列進行排序
TableView類提供了內建的列排序功能,用戶可以通過單擊列標題來改變數據的順序。第一次單擊按升序排列,再一單擊按降序排列,第三次單擊取消排序。默認不進行排序。
用戶可以對錶進行多列排序,並可指定在排序時哪一列是排序的主要列。要對多列進行排序,用戶可以在要排序的列標題是單擊時按下SHIFT鍵。
在圖13-7中,已對“姓”這一列按升序進行了排列,可以再對“名”這一列按降序進行排列。注意第一列比第二列具有更高的優先級,是主排序列。
圖13-7 多列排序
如果是開發人員,你可以在程序中通過setSortAscending方法來設置各列的排序選項,爲此方法傳入“true”的參數爲升序排列,如果參數爲“false”則爲降序。
你也可以通過從TableView.sortOrder觀察者列表中增加或刪除TableColoumn實例來指定對哪一列進行排序,列表中列的順序代表排序的優先順序(例如:第一個優先於第二個)。
如果要禁止排序,可對列調用setSortable(false)方法。
你還可以使用JavaFX SDK提供的FilteredList類爲你的表格實現一個過濾器。這個類包裝了ObservableList類,提供了方便的過濾功能。創建過濾器時,你要指定數據源和過濾準則(適配器)。它支持LIVE和BATCH過濾模式。FilterableList.FiterMode.LIVE模式爲自動過濾。而FilterableList.FiterMode.BATCH模式通過FilterableList.fliter方法的要求來過濾。
SortedList類是ObservableList類的另一種包裝器,當新數據加入到表中時,你可以使用它來排序。
在表中編輯數據
TableView類不僅可以繪製表格化的數據,它還提供了編輯的能力。你可以使用TableView.edit(TablePosition)方法來啓動編輯器。要取消編輯,可以爲編輯器傳一個null值。你還可以通過TableCell類來編輯表中的數據,如例13-9所示:
例13-9 實現單元格編輯
- class EditingCell extends TableCell<String> {
- private final Label label;
- private TextBox textBox;
- public EditingCell() {
- this.label = new Label();
- }
- @Override
- public void startEdit() {
- super.startEdit();
- if (isEmpty()) {
- return;
- }
- if (textBox == null) {
- createTextBox();
- } else {
- textBox.setText(getItem());
- }
- setNode(textBox);
- textBox.requestFocus();
- textBox.selectAll();
- }
- @Override
- public void cancelEdit() {
- super.cancelEdit();
- setNode(label);
- }
- @Override
- public void commitEdit(Stringt) {
- super.commitEdit(t);
- setNode(label);
- }
- @Override
- public void updateItem(Stringitem, boolean empty) {
- super.updateItem(item,empty);
- if (!isEmpty()) {
- if (textBox != null){
- textBox.setText(item);
- }
- label.setText(item);
- setNode(label);
- }
- }
- private void createTextBox(){
- textBox = newTextBox(getItem());
- textBox.setOnKeyReleased(new EventHandler<KeyEvent>() {
- @Override public void handle(KeyEventt) {
- if (t.getCode()== KeyCode.ENTER) {
- commitEdit(textBox.getRawText());
- } else if(t.getCode() == KeyCode.ESCAPE) {
- cancelEdit();
- }
- }
- });
- }
- }
TableColoumn類的setCellFactory方法用以爲單元格安裝一個自定義的單元格構造器,這個構造器的作用是在需要時返加新的TableCell實例。例13-10展示瞭如何爲firstName、lastName和emailCol列實現一個單元格構造器。
例13-10 使用單元格構造器
- Callback<TableColumn, TableCell> cellFactory =
- new Callback<TableColumn,TableCell>() {
- public TableCellcall(TableColumn p) {
- return new EditingCell();
- }
- };
如例13-11一樣,使用TableColumn類的setOnEditCommit方法來處理單元格內容的變化。這個方法識別修改內容,檢索新的值,然後替換可觀察列表中當前元素的數據。
例13-11 處理表中的數據編輯
- //Modifying the firstName property
- firstNameCol.setOnEditCommit(newEventHandler<EditEvent<String>>() {
- @Override public voidhandle(EditEvent<String> t) {
- ((Person)t.getTableView().getItems().get(
- t.getTablePosition().getRow())).setFirstName(t.getNewValue());
- }
- });
- //Modifying the lastName property
- lastNameCol.setOnEditCommit(newEventHandler<EditEvent<String>>() {
- @Override public voidhandle(EditEvent<String> t) {
- ((Person)t.getTableView().getItems().get(
- t.getTablePosition().getRow())).setLastName(t.getNewValue());
- }
- });
- //Modifying the email property
- emailCol.setOnEditCommit(new EventHandler<EditEvent<String>>(){
- @Override public voidhandle(EditEvent<String> t) {
- ((Person)t.getTableView().getItems().get(
- t.getTablePosition().getRow())).setEmail(t.getNewValue());
- }
- });
在圖13-8中,用戶正編輯MichaelBrown的名字。要編輯單元格,用戶在單元格中鍵入新的值,然後按下Enter鍵。如果不按下Enter鍵,單元格的值不會被修改。這種行爲主要是因爲單元格編輯器實現了TextBox類。
圖13-8 單元格編輯
相關API
- TableView
- TableColumn
- TableCell
- TextBox
- Button
14 分隔條
JavaFX API提供的Separator類代表一個水平或垂直的線條。它用來在用戶界面中分隔組件,它不會產生任何動作。不過,你可以對它進行樣式化,增加特效或創建動畫。默認地,分隔條是水平的,但可以用setOrientation方法來改變方向。
創建分隔條
例14-1的代碼段創建了一個水平分隔條和一個垂直分隔條。
例14-1 水平和垂直分隔條
- //Horizontal separator
- Separator separator1 = new Separator();
- //Vertical separator
- Separator separator2 = new Separator();
- separator2.setOrientation(Orientation.VERTICAL);
Separator類是Node類的子類,因此,它繼承了Node類的所有實例變更。
一般而言,分隔條用來分隔UI組件的組,研究一下例14-2的代碼段,它將春季複選框和夏季複選框分開。
例14-2 在複選類型間使用分隔條
- final String[] names = new String[]{"March", "April","May",
- "June","July", "August"};
- final CheckBox[] cbs = new CheckBox[names.length];
- final Separator separator = new Separator();
- final VBox vbox = new VBox();
- for (int i = 0; i < names.length; i++) {
- cbs[i] = new CheckBox(names[i]);
- }
- separator.setMaxWidth(40);
- separator.setAlignment(Pos.CENTER_LEFT);
- vbox.getChildren().addAll(cbs);
- vbox.setSpacing(5);
- vbox.getChildren().add(3, separator);
這段代碼增加到程序中後,產生圖14-1的控件樣式。
圖14-1 複選框和分隔條
分隔條佔用了分配給它的水平或垂直位置。setMaxWidth方法用於定義實際的寬度,setVPos方法在已分配的佈局空間內指定分隔條的垂直位置。相應的,你可以通過setHpos方法設置水平分隔線的位置。
在例14-2中,分隔條通過黨規的add(index,node)方法添加到垂直框中。你可以在你的應用程序中使用這種方法在UI創建後或動態改變後來增加分隔條。
在程序中爲UI添加分隔條
如前所述,分隔條於來分隔UI控件組,你也可以使用它來構造用戶界面。考慮一下創建一個如圖14-2所示的繪製一個天氣預報信息的情況。
圖14-2 用分隔條構建天氣預報數據
對圖14-2所示的程序來說,分隔條用於分隔Label和ImageView對象,請研究一下例14-3所示的程序的代碼。
例14-3 在天氣預報程序中使用分隔條
- import javafx.application.Application;
- import javafx.geometry.Insets;
- import javafx.geometry.VPos;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.*;
- import javafx.scene.effect.DropShadow;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.GridPane;
- import javafx.scene.text.Font;
- import javafx.stage.Stage;
- public class Main extends Application {
- Button button3 = newButton("Decline");
- DropShadow shadow = newDropShadow();
- Label caption = newLabel("Weather Forecast");
- Label friday = newLabel("Friday");
- Label saturday = newLabel("Saturday");
- Label sunday = newLabel("Sunday");
- @Override
- public void start(Stage stage) {
- Group root = new Group();
- Scene scene = new Scene(root,500, 300);
- stage.setScene(scene);
- stage.setTitle("Separator Sample");
- GridPane grid = newGridPane();
- grid.setPadding(newInsets(10, 10, 10, 10));
- grid.setVgap(2);
- grid.setHgap(5);
- scene.setRoot(grid);
- Image cloudImage = newImage(getClass().getResourceAsStream("cloud.jpg"));
- Image sunImage = newImage(getClass().getResourceAsStream("sun.jpg"));
- caption.setFont(Font.font("Verdana", 20));
- GridPane.setConstraints(caption, 0, 0);
- grid.setColumnSpan(caption,8);
- grid.getChildren().add(caption);
- final Separator sepHor = newSeparator();
- sepHor.setVpos(VPos.CENTER);
- GridPane.setConstraints(sepHor, 0, 1);
- grid.setColumnSpan(sepHor,7);
- grid.getChildren().add(sepHor);
- friday.setFont(Font.font("Verdana", 18));
- GridPane.setConstraints(friday, 0, 2);
- grid.setColumnSpan(friday,2);
- grid.getChildren().add(friday);
- final Separator sepVert1 =new Separator();
- sepVert1.setOrientation(Orientation.VERTICAL);
- sepVert1.setVpos(VPos.CENTER);
- sepVert1.setPrefHeight(80);
- GridPane.setConstraints(sepVert1, 2, 2);
- grid.setRowSpan(sepVert1, 2);
- grid.getChildren().add(sepVert1);
- saturday.setFont(Font.font("Verdana",18));
- GridPane.setConstraints(saturday, 3, 2);
- grid.setColumnSpan(saturday,2);
- grid.getChildren().add(saturday);
- final Separator sepVert2 =new Separator();
- sepVert2.setOrientation(Orientation.VERTICAL);
- sepVert2.setVpos(VPos.CENTER);
- sepVert2.setPrefHeight(80);
- GridPane.setConstraints(sepVert2, 5, 2);
- grid.setRowSpan(sepVert2, 2);
- grid.getChildren().add(sepVert2);
- sunday.setFont(Font.font("Verdana", 18));
- GridPane.setConstraints(sunday, 6, 2);
- grid.setColumnSpan(sunday,2);
- grid.getChildren().add(sunday);
- final ImageView cloud = newImageView(cloudImage);
- GridPane.setConstraints(cloud,0, 3);
- grid.getChildren().add(cloud);
- final Label t1 = newLabel("16");
- t1.setFont(Font.font("Verdana", 20));
- GridPane.setConstraints(t1,1, 3);
- grid.getChildren().add(t1);
- final ImageView sun1 = newImageView(sunImage);
- GridPane.setConstraints(sun1,3, 3);
- grid.getChildren().add(sun1);
- final Label t2 = newLabel("18");
- t2.setFont(Font.font("Verdana", 20));
- GridPane.setConstraints(t2,4, 3);
- grid.getChildren().add(t2);
- final ImageView sun2 = newImageView(sunImage);
- GridPane.setConstraints(sun2,6, 3);
- grid.getChildren().add(sun2);
- final Label t3 = new Label("20");
- t3.setFont(Font.font("Verdana", 20));
- GridPane.setConstraints(t3,7, 3);
- grid.getChildren().add(t3);
- stage.setVisible(true);
- }
- public static void main(String[]args) {
- Application.launch(args);
- }
- }
這個程序同時使用了水平和垂直分隔條,並且它們跨越了GridPane容器的行和列,在你的程序中,也可以爲分隔條設定推薦的長度(對水平分隔條是長度,對垂直分隔條是高度),以便當用戶界面的大小被調整時它能動態地改變。你還可以通過Separator對象提供的CSS類來爲分隔條增加額外的可視化效果。
樣式化分隔條
要爲例14-3中的所有分隔條使用相同的樣式,你可以創建一個CSS文件(例如:controlStyle.css),然後將它保存在與主類(main class)相同的包內。例14-4演示了可以增加到controlSytle文件中的CSS類。
例14-4 使用CSS類樣式化分隔條
- /*controlStyle.css */
- .separator{
- -fx-background-color: #e79423;
- -fx-background-radius: 2;
- -fx-background-insets: -2;
- }
你可以在程序中通過getStylesheets方法來啓用樣式化的分隔條。如例14-5所示:
例14-5 在JavaFX程序中啓用樣式表
- scene.getStylesheets().add("/separatorsample/controlStyle.css");
圖14-3 樣式化的分隔條
相關API
- Separator
- JavaFX CSS Specification
15 滑動條
滑動條代表一種在表現和交互一定範圍內的數值的控件,它包括一個跟蹤條和一個滑動塊。也可以包含表示數值範圍的刻度和刻度標籤。圖15-1展示了典型的滑動條和它的主要構件。
圖15-1 滑動條的構件
創建滑動條
讓我們花點時間來研究一下例15-1的代碼段,它產生的輸出如圖15-1所示。
例15-1 創建滑動條
- Slider slider = new Slider();
- slider.setMin(0);
- slider.setMax(100);
- slider.setValue(40);
- slider.setShowTickLabels(true);
- slider.setShowTickMarks(true);
- slider.setMajorTickUnit(50);
- slider.setMinorTickCount(5);
- slider.setBlockIncrement(10);
兩個布爾方法,setShowTickMarks和setShowTickLabels定義了滑動塊的外觀。在例15-1中,刻度和標籤已經啓用。另外,主要刻度的間距設爲了50,兩個主要刻度間的次要刻度的間距設爲了5。你可以將setSnapToTicks方法的參數設爲true來保證滑動塊的值正好位於刻度上。
setBlockIncrement方法定義了用戶在跟蹤條上單擊時滑動塊移動的距離。在例15-1中,這個值是10,滑動塊會向單擊方向移動10個單位。
在圖形程序中使用滑動條
現在查看一下圖15-2,這個程序使用了三個滑動條來編輯圖片的繪製屬性。每個滑動條調整一個實際的可視化屬性:透明度,棕褐色調值和比例因子。
圖15-2 三個滑動條
例15-2展示了這個程序的源代碼
例15-2 滑動條例子
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.geometry.Insets;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Label;
- import javafx.scene.control.Slider;
- import javafx.scene.effect.SepiaTone;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.GridPane;
- import javafx.scene.paint.Color;
- import javafx.stage.Stage;
- public class Main extends Application {
- final Slider opacityLevel = newSlider(0, 1, 1);
- final Slider sepiaTone = newSlider(0, 1, 1);
- final Slider scaling = new Slider(0.5, 1, 1);
- final Image image = new Image(getClass().getResourceAsStream(
- "cappuccino.jpg")
- );
- final Label opacityCaption = newLabel("Opacity Level:");
- final Label sepiaCaption = newLabel("Sepia Tone:");
- final Label scalingCaption = newLabel("Scaling Factor:");
- final Label opacityValue = newLabel(
- Double.toString(opacityLevel.getValue()));
- final Label sepiaValue = newLabel(
- Double.toString(sepiaTone.getValue()));
- final Label scalingValue = newLabel(
- Double.toString(scaling.getValue()));
- final static Color textColor =Color.WHITE;
- final static SepiaTonesepiaEffect = new SepiaTone();
- @Override
- public void start(Stage stage) {
- Group root = new Group();
- Scene scene = new Scene(root,480, 400);
- stage.setScene(scene);
- stage.setTitle("SliderSample");
- scene.setFill(Color.BLACK);
- GridPane grid = newGridPane();
- grid.setPadding(newInsets(10, 10, 10, 10));
- grid.setVgap(10);
- grid.setHgap(70);
- final ImageView cappuccino =new ImageView (image);
- cappuccino.setEffect(sepiaEffect);
- GridPane.setConstraints(cappuccino, 0, 0);
- grid.setColumnSpan(cappuccino, 3);
- grid.getChildren().add(cappuccino);
- scene.setRoot(grid);
- opacityCaption.setTextFill(textColor);
- GridPane.setConstraints(opacityCaption, 0, 1);
- grid.getChildren().add(opacityCaption);
- opacityLevel.valueProperty().addListener(newChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val,Number new_val) {
- cappuccino.setOpacity(new_val.doubleValue());
- opacityValue.setText(String.format("%.2f", new_val));
- }
- });
- GridPane.setConstraints(opacityLevel, 1, 1);
- grid.getChildren().add(opacityLevel);
- opacityValue.setTextFill(textColor);
- GridPane.setConstraints(opacityValue, 2, 1);
- grid.getChildren().add(opacityValue);
- sepiaCaption.setTextFill(textColor);
- GridPane.setConstraints(sepiaCaption, 0, 2);
- grid.getChildren().add(sepiaCaption);
- sepiaTone.valueProperty().addListener(new ChangeListener<Number>(){
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val,Number new_val) {
- sepiaEffect.setLevel(new_val.doubleValue());
- sepiaValue.setText(String.format("%.2f", new_val));
- }
- });
- GridPane.setConstraints(sepiaTone, 1, 2);
- grid.getChildren().add(sepiaTone);
- sepiaValue.setTextFill(textColor);
- GridPane.setConstraints(sepiaValue, 2, 2);
- grid.getChildren().add(sepiaValue);
- scalingCaption.setTextFill(textColor);
- GridPane.setConstraints(scalingCaption, 0, 3);
- grid.getChildren().add(scalingCaption);
- scaling.valueProperty().addListener(new ChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val,Number new_val) {
- cappuccino.setScaleX(new_val.doubleValue());
- cappuccino.setScaleY(new_val.doubleValue());
- scalingValue.setText(String.format("%.2f", new_val));
- }
- });
- GridPane.setConstraints(scaling, 1, 3);
- grid.getChildren().add(scaling);
- scalingValue.setTextFill(textColor);
- GridPane.setConstraints(scalingValue, 2, 3);
- grid.getChildren().add(scalingValue);
- stage.setVisible(true);
- }
- public static void main(String[]args) {
- Application.launch(args);
- }
- }
ImageView對象的透明度屬性根據名爲透明級的第一個滑動條的值來改變,棕褐色調特效的級別根據棕褐色調滑動條的值來確定,第三個滑動條通過傳遞參數給setScaleX和setScaleY方法定義了圖片的比例因子。
例13-5的代碼段演示了由滑動條的getValue方法返回的Double值轉爲String的方法,還展示了應用格式化將滑動條的浮點值按兩位小數來繪製的方法。
例15-3 格式化繪製滑動條的值
- scalingValue.setText((Double.toString(value)).format("%.2f",value));
下一步可以通過應用可視化特效或CSS樣式來改善滑動條的樣式。
相關API
- Slider
- SpiaTone
16 進度條和進度指示器
ProgressIndicator類和它的直接子類ProgressBar提供了指示特定任務正在運行並檢測任務運行進度的能力,ProgressBar將進度繪製爲一個進度條,ProgressIndicator將進度繪製爲一個動態改奕的餅圖,如圖16-1所示:
圖16-1 進度條和進度指示器
創建進度控件
可在你的應用程序中使用例16-1中的代碼段來插入一個進度控件。
例16-1 進度條和進度指示器的實現
- ProgressBar pb = new ProgressBar(0.6);
- ProgressIndicator pi = new ProgressIndicator(0.6);
你也可以通過無參數的空構造器來創建進度控件,在這種情況下,可以通過使用setProgress方法來賦值。另上種實例化進度控件的方法是使用ProgressBarBuilder類,它通過build和progress提供了相同的方法。關於本類的更多信息,請參閱API文檔。
有時程序無法知道一個任務的準確的完成時間,在這種情況下,進度控件提供一個動態指示直到任務的長度能準確決定。圖16-2展示了根據不同的進度值繪製的各種不同的進度狀態。
圖16-2 進度控件的不同狀態
例16-2 展示了這個程序的源代碼
例16-2 開啓進度控件的不同狀態
- import javafx.application.Application;
- import javafx.geometry.Pos;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Label;
- import javafx.scene.control.ProgressBar;
- import javafx.scene.control.ProgressIndicator;
- import javafx.scene.layout.HBox;
- import javafx.scene.layout.VBox;
- import javafx.stage.Stage;
- public class Main extendsApplication {
- final Float[] values = new Float[] {-1.0f, 0f, 0.6f, 1.0f};
- final Label [] labels = new Label[values.length];
- final ProgressBar[] pbs = new ProgressBar[values.length];
- final ProgressIndicator[] pins = new ProgressIndicator[values.length];
- final HBox hbs [] = new HBox [values.length];
- @Override
- public void start(Stage stage) {
- Group root = new Group();
- Scene scene = new Scene(root,300, 150);
- scene.getStylesheets().add("/progresssample/Style.css");
- stage.setScene(scene);
- stage.setTitle("ProgressControls");
- for (int i = 0; i <values.length; i++) {
- final Label label =labels[i] = new Label();
- label.setText("progress:" + values[i]);
- final ProgressBar pb =pbs[i] = new ProgressBar();
- pb.setProgress(values[i]);
- final ProgressIndicatorpin = pins[i] = new ProgressIndicator();
- pin.setProgress(values[i]);
- final HBox hb = hbs[i] =new HBox();
- hb.setSpacing(5);
- hb.setAlignment(Pos.CENTER);
- hb.getChildren().addAll(label, pb, pin);
- }
- final VBox vb = new VBox();
- vb.setSpacing(5);
- vb.getChildren().addAll(hbs);
- scene.setRoot(vb);
- stage.setVisible(true);
- }
- public static void main(String[]args) {
- Application.launch(args);
- }
- }
設爲0到1之間的正值用來表表進度控件的百分比,例如:0.4表示40%。負值表示進度條的完成時間不確定。使用isIndeterminate方法來檢相進度控制是否屬不確定狀態。
進度指示器
圖16-2展現了進度控制的一般狀態,在現實的應用程序中,進度值可以通過其他其他UI組件獲取。
研究一下例16-3,學習如何根據滑動條位置爲進度條和進度指示器設定值。
例16-3 從滑動條接受進度值
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.geometry.Pos;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.ProgressBar;
- import javafx.scene.control.ProgressIndicator;
- import javafx.scene.control.Slider;
- import javafx.scene.layout.HBox;
- import javafx.stage.Stage;
- public class Main extendsApplication {
- @Override
- public void start(Stage stage) {
- Group root = new Group();
- Scene scene = newScene(root);
- stage.setScene(scene);
- stage.setTitle("ProgressControls");
- final Slider slider = newSlider();
- slider.setMin(0);
- slider.setMax(50);
- final ProgressBar pb = newProgressBar(0);
- final ProgressIndicator pi =new ProgressIndicator(0);
- slider.valueProperty().addListener(new ChangeListener<Number>() {
- public voidchanged(ObservableValue<? extends Number> ov,
- Number old_val,Number new_val) {
- pb.setProgress(new_val.doubleValue()/50);
- pi.setProgress(new_val.doubleValue()/50);
- }
- });
- final HBox hb = new HBox();
- hb.setSpacing(5);
- hb.setAlignment(Pos.CENTER);
- hb.getChildren().addAll(slider, pb, pi);
- scene.setRoot(hb);
- stage.show();
- }
- public static void main(String[]args) {
- launch(args);
- }
- }
可以編譯並運行這個程序,產生的輸出如圖16-3所示。
圖16-3 通過滑動條設定進度指示
ChangeListenner對象決定滑動條的值是否改變,並計算進度條和進度指示器的值,以更進度控制的值範圍在0.0到1.0之間。
相關API
- ProgressBar
- ProgressIndicator
17 超鏈接控件
超鏈接類代表另一種類型的Labeled控件,用於將格式文本作爲超鏈接使用。圖17-1演示了超鏈接實現的三種狀態。
圖17-1 超鏈接控件的三種狀態
創建超鏈接
產生超鏈接的代碼如例17-1所示。
例17-1 典型的超鏈接
- Hyperlink link = new Hyperlink();
- link.setText("http://example.com");
- link.setOnAction(new EventHandler<ActionEvent>() {
- @Override
- public void handle(ActionEvent e){
- System.out.println("Thislink is clicked");
- }
- });
setText實例方法定義了超鏈接的標籤,由於超鏈接繼承自Labeled類,你可以設置特定的字體和文本。setOnAction方法設定了動作,當超鏈接被單擊時,動作方法會被調用,與Button類相應方法類似。在例17-1中,此方法被限制用於打印一個字串,然而,你可以設爲實現一個更通用的任務。
鏈接本地內容
圖17-2的程序繪製了本地目錄中的圖像。
圖17-2 瀏覽圖像
該程序的代碼如下。
例17-2 使用超鏈接瀏覽圖像
- import javafx.application.Application;
- import javafx.event.ActionEvent;
- import javafx.event.EventHandler;
- import javafx.scene.*;
- import javafx.scene.control.*;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.VBox;
- import javafx.stage.Stage;
- public class Main extends Application {
- final static String[] imageFiles = new String[]{
- "product.png",
- "education.png",
- "partners.png",
- "support.png"
- };
- final static String[] captions =new String[]{
- "Products",
- "Education",
- "Partners",
- "Support"
- };
- final ImageView selectedImage =new ImageView();
- final ScrollPane list = newScrollPane();
- final Hyperlink[] hpls = newHyperlink[captions.length];
- final Image[] images = newImage[imageFiles.length];
- public static void main(String[]args) {
- Application.launch(args);
- }
- @Override
- public void start(Stage stage) {
- Scene scene = new Scene(newGroup());
- stage.setTitle("Hyperlink Sample");
- stage.setWidth(300);
- stage.setHeight(200);
- selectedImage.setLayoutX(100);
- selectedImage.setLayoutY(10);
- for (int i = 0; i <captions.length; i++) {
- final Hyperlink hpl =hpls[i] = new Hyperlink(captions[i]);
- final Image image =images[i] = new Image(
- getClass().getResourceAsStream(imageFiles[i])
- );
- hpl.setOnAction(newEventHandler<ActionEvent>() {
- @Override
- public voidhandle(ActionEvent e) {
- selectedImage.setImage(image);
- }
- });
- }
- final Button button = newButton("Refresh links");
- button.setOnAction(newEventHandler<ActionEvent>() {
- @Override
- public voidhandle(ActionEvent e) {
- for (int i = 0; i< captions.length; i++) {
- hpls[i].setVisited(false);
- selectedImage.setImage(null);
- }
- }
- });
- VBox vbox = new VBox();
- vbox.getChildren().addAll(hpls);
- vbox.getChildren().add(button);
- vbox.setSpacing(5);
- ((Group)scene.getRoot()).getChildren().addAll(vbox, selectedImage);
- stage.setScene(scene);
- stage.show();
- }
- }
該程序在for循環內創建了4個超鏈接對象,供每個超鏈接調用的setOnAction方法定義了用戶單擊超鏈接時的行爲。在本例中,inages數組中的相應圖像被賦給了selectedImage變量。
當用戶單擊超鏈接時,它被標記爲已訪問。你可以使用超鏈接類的setVisited方法來更新鏈接。例17-3的代碼完成這項工作。
例17-3 更新鏈接
- final Button button = new Button("Refresh links");
- button.setOnAction(new EventHandler<ActionEvent>() {
- @Override
- public void handle(ActionEvent e){
- for (int i = 0; i <captions.length; i++) {
- hpls[i].setVisited(false);
- selectedImage.setImage(null);
- }
- }
- });
單擊更新鏈接按鈕時,所有的超鏈接變爲未訪問狀態,如圖17-3所示。
圖17-3 未訪問鏈接
由於超鏈接類繼承自Labeled類,你不公可以設定文本標籤,還可以設置圖像。下節提供的代碼使用了文本和圖像來創建超鏈接,並加載了遠程HTML頁面。
鏈接遠程內容
你可以在程序中場景嵌入WebView瀏覽器來繪製HTML內容。WebView組件提供了基本的Web頁面瀏覽功能。它可以繪製Web頁面內容,提供如超鏈接交互和執行JavaScript代碼的能力。
研究一下例17-4的程序,它創建了4個有文本和圖像的超鏈接,當超鏈接被單擊時,相應的值作爲URL鏈接至嵌入的瀏覽器中。
例17-4 調用遠程Web頁面。
- import javafx.application.Application;
- import javafx.event.ActionEvent;
- import javafx.event.EventHandler;
- import javafx.scene.*;
- import javafx.scene.control.*;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.HBox;
- import javafx.scene.layout.Priority;
- import javafx.scene.layout.VBox;
- import javafx.scene.text.Font;
- import javafx.scene.web.WebEngine;
- import javafx.scene.web.WebView;
- import javafx.stage.Stage;
- public class Main extends Application {
- final static String[] imageFiles= new String[]{
- "product.png",
- "education.png",
- "partners.png",
- "support.png"
- };
- final static String[] captions =new String[]{
- "Products",
- "Education",
- "Partners",
- "Support"
- };
- final static String[] urls = newString[]{
- "http://www.oracle.com/us/products/index.html",
- "http://education.oracle.com/",
- "http://www.oracle.com/partners/index.html",
- "http://www.oracle.com/us/support/index.html"
- };
- final ImageView selectedImage =new ImageView();
- final Hyperlink[] hpls = newHyperlink[captions.length];
- final Image[] images = newImage[imageFiles.length];
- public static void main(String[]args){
- launch(args);
- }
- @Override
- public void start(Stage stage) {
- VBox vbox = new VBox();
- Scene scene = newScene(vbox);
- stage.setTitle("Hyperlink Sample");
- stage.setWidth(570);
- stage.setHeight(550);
- selectedImage.setLayoutX(100);
- selectedImage.setLayoutY(10);
- final WebView browser = newWebView();
- final WebEngine webEngine =browser.getEngine();
- for (int i = 0; i <captions.length; i++) {
- final Hyperlink hpl =hpls[i] = new Hyperlink(captions[i]);
- final Image image =images[i] =
- newImage(getClass().getResourceAsStream(imageFiles[i]));
- hpl.setGraphic(newImageView (image));
- hpl.setFont(Font.font("Arial",14));
- final String url =urls[i];
- hpl.setOnAction(newEventHandler<ActionEvent>() {
- @Override
- public voidhandle(ActionEvent e) {
- webEngine.load(url);
- }
- });
- }
- HBox hbox = new HBox();
- hbox.getChildren().addAll(hpls);
- vbox.getChildren().addAll(hbox, browser);
- VBox.setVgrow(browser,Priority.ALWAYS);
- stage.setScene(scene);
- stage.show();
- }
- }
超鏈接在for循環中被創建,如例17-2一樣。超鏈接的動作通過ruls數組中相應的URL訪問頁面,顯示於內嵌瀏覽器的WebEngine對象中。
編譯並運行程序,產生的窗口如圖17-4所示。
圖17-4 從Oracle專業網站調用頁面
相關API
- Hyperlink
- Labeled
- WebView
- WebEngine
18 工具提示
工具提示代表一種常規的UI組件,一般用來顯不關於UI控件的其他信息。當鼠標在控件上懸停時,工具提示會顯示出來。工具提示只能通過調用控件的setTooltip來設定。
工具提示有二種狀態:活動和顯示。鼠標在控件上時,它被激活,當工具提供處於顯示狀態時,它才實際出現。顯示出來的工具提示同時也時活動的。從活動到顯示通過會有一定的延遲時間。
具有工具提示的密碼框如圖18-1所示。
圖18-1 加入工具提示的密碼框
創建工具提示
研究一下例18-1中的代碼,它創建了前述的圖中所展現的帶有工具提示的密碼框。
例18-1 爲密碼框增加工具提示
- final PasswordField pf = new PasswordField();
- final Tooltip tooltip = new Tooltip();
- tooltip.setText(
- "\nYour password mustbe\n" +
- "at least 8 characters inlength\n" +
- );
- pf.setTooltip(tooltip);
javafx.scene.control包中的每個控件都有setTooltip方法用於設定工具提示。你可以通過Tooptip類的構造器或使用setText方法爲工具提示指定提示文本。
由於工具所示類擴展自Labeled類,你還可以設定圖標。例18-2的代碼爲密碼框設定了帶有圖標的工具提示。
例18-2 爲工具提示增加圖標
- Image image = new Image(
- getClass().getResourceAsStream("warn.png")
- );
- tooltip.setGraphic(new ImageView(image));
將上述代碼增加到程序中編譯運行後,圖18-2會出現。
圖18-2 帶圖標的工具提示
工具提示不僅可以包含輔助信息,還可以表現數據。
在工具提示中表現程序數據
圖18-3的程序使用工具提示中的信息計算旅館費用。
圖18-3 計算旅館費用
每個複選框都有一個相應的工具提示,每個工具提示顯示了實際的登記費用。如果用戶選擇了一個複選框,相應的值增加到彙總費用中,反之則從彙總費用中扣除。
例18-3 使用工具提示來計算旅館費用
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.geometry.Insets;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.CheckBox;
- import javafx.scene.control.Label;
- import javafx.scene.control.Tooltip;
- import javafx.scene.layout.HBox;
- import javafx.scene.layout.VBox;
- import javafx.scene.text.Font;
- import javafx.stage.Stage;
- public class Main extends Application {
- final static String[] rooms = newString[]{
- "Accommodation(BB)",
- "Half Board",
- "Late Check-out",
- "Extra Bed"
- };
- final static Integer[] rates =new Integer[]{
- 100, 20, 10, 30
- };
- final CheckBox[] cbs = newCheckBox[rooms.length];
- final Label total = newLabel("Total: $0");
- Integer sum = 0;
- public static void main(String[]args) {
- launch(args);
- }
- @Override
- public void start(Stage stage) {
- Scene scene = new Scene(newGroup());
- stage.setTitle("TooltipSample");
- stage.setWidth(300);
- stage.setHeight(150);
- total.setFont(newFont("Arial", 20));
- for (int i = 0; i <rooms.length; i++) {
- final CheckBox cb =cbs[i] = new CheckBox(rooms[i]);
- final Integer rate =rates[i];
- final Tooltip tooltip =new Tooltip("{1}quot; + rates[i].toString());
- tooltip.setFont(newFont("Arial", 16));
- cb.setTooltip(tooltip);
- cb.selectedProperty().addListener(new ChangeListener<Boolean>() {
- public voidchanged(ObservableValue<? extends Boolean> ov,
- Boolean old_val,Boolean new_val) {
- if(cb.isSelected()) {
- sum = sum +rate;
- } else {
- sum = sum -rate;
- }
- total.setText("Total: {1}quot; + sum.toString());
- }
- });
- }
- VBox vbox = new VBox();
- vbox.getChildren().addAll(cbs);
- vbox.setSpacing(5);
- HBox root = new HBox();
- root.getChildren().add(vbox);
- root.getChildren().add(total);
- root.setSpacing(40);
- root.setPadding(newInsets(20, 10, 10, 20));
- ((Group) scene.getRoot()).getChildren().add(root);
- stage.setScene(scene);
- stage.show();
- }
- }
例18-4中的代碼行在例18-3中被用來創建工具提示並將文本賦予它,選項價格的Integer值轉換爲String值。
例18-4 爲工具提示設定值
- final Tooltip tooltip = new Tooltip("{1}quot; + rates[i].toString())
你可通過使用CSS爲工具提示設定樣式。
相關API
- Tooltip
- Labeled
19 HTML編輯器
HTMLEditor控件是一個全功能的富文本編輯器,除基本編輯功能外,它不支持以下特點:
- 文本格式化,包括加粗、傾斜、下劃線、穿透線
- 段落設定,如格式和字體
- 前景和背景色
- 文本縮進
- 符號列表和數字列表
- 文本對齊
- 水平標尺
- 拷貝和粘貼
圖19-1展示了增加到JavaFX程序中的富文本編輯器
圖19-1 HTML編輯器
HTMLEditor類以HTML字串的形式表示編輯內容,例如:在圖19-1的編輯器中鍵入的內容用下列的字串表示,"<html><head></head><bodycontenteditable="true"><h1>Heading</h1><div><u>Text</u>,some text</div></body></html>."
由於HTMLEditor類屬Node類的子類,你可以對HTMLEditor類的實例應用可視效果或變換。
增加HTML編輯器
和其他UI控件一樣,HTMLEditor組件必須增加到場景中,以便它可以出現在應用程序中。你可以像例19-1所示的一樣直接將它增加到場景中,也可以像其他程序一樣通過佈局容器來增加。
例19-1 向JavaFX程序中增加HTML編輯器
- import javafx.application.Application;
- import javafx.scene.Scene;
- import javafx.scene.web.HTMLEditor;
- import javafx.stage.Stage;
- public class HTMLEditorSample extends Application {
- @Override
- public void start(Stage stage) {
- stage.setTitle("HTMLEditor Sample");
- stage.setWidth(400);
- stage.setHeight(300);
- final HTMLEditor htmlEditor =new HTMLEditor();
- htmlEditor.setPrefHeight(245);
- Scene scene = newScene(htmlEditor);
- stage.setScene(scene);
- stage.show();
- }
- public static void main(String[]args) {
- launch(args);
- }
- }
編譯並運行上述代碼,產生的窗口如圖19-2所示。
圖19-2HTMLEditor組件的初始視圖
該組件實現了格式化工具條,你不能關閉它。但你仍然可以通過CSS樣式定製外觀,如例19-2所示。
例19-2 樣式化HTML編輯器
- htmlEditor.setStyle(
- "-fx-font: 12 cambria;"
- + "-fx-border-color: brown;"
- + "-fx-border-style:dotted;"
- + "-fx-border-width:2;"
- );
當上述代碼增加到例19-1中後,編輯器變化了,如圖19-3所示。
例19-3 另一種HTMLEditor視圖
應用的樣式改變了組件的邊界和工具條的字體。
HTMLEditor類提供了一個方法來定義出現在編輯區的內容,使用setHtmlText方法,如例19-3所示,可以設定編輯器中的初始文本。
例19-3 設定文本內容
- private final String INITIAL_TEXT = "<html><body>Loremipsum dolor sit "
- + "amet, consecteturadipiscing elit. Nam tortor felis, pulvinar "
- + "in scelerisque cursus,pulvinar at ante. Nulla consequat"
- + "congue lectus in sodales.Nullam eu est a felis ornare "
- + "bibendum et nec tellus.Vivamus non metus tempus augue auctor "
- + "ornare. Duis pulvinarjusto ac purus adipiscing pulvinar. "
- + "Integer congue faucibusdapibus. Integer id nisl ut elit "
- + "aliquam sagittis gravidaeu dolor. Etiam sit amet ipsum "
- +"sem.</body></html>";
- htmlEditor.setHtmlText(INITIAL_TEXT);
圖19-4 具有預定義內容的HTMLEditor
你可以在這些字串中使用HTML標籤來爲初始繪製的內容使用特定的格式。
使用HTML編輯器來創建用戶界面
你可以使用HTMLEditor控件在你的應用程序中實現典型的用戶界面。例如,你可實現即時通訊服務,email客戶端或者內容管理系統。表現在很多email客端程序常見的信息綜合窗口的用戶界面。
例19-4 將HTMLEditor增加到Email客戶端的UI中
- import javafx.application.Application;
- import javafx.collections.FXCollections;
- import javafx.geometry.Insets;
- import javafx.geometry.Pos;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.*;
- import javafx.scene.layout.GridPane;
- import javafx.scene.layout.VBox;
- import javafx.scene.web.HTMLEditor;
- import javafx.stage.Stage;
- public class HTMLEditorSample extends Application {
- @Override
- public void start(Stage stage) {
- stage.setTitle("MessageComposing");
- stage.setWidth(500);
- stage.setHeight(500);
- Scene scene = new Scene(newGroup());
- final VBox root = newVBox();
- root.setPadding(new Insets(8,8, 8, 8));
- root.setSpacing(5);
- root.setAlignment(Pos.BOTTOM_LEFT);
- final GridPane grid = newGridPane();
- grid.setVgap(5);
- grid.setHgap(10);
- final ChoiceBox sendTo =
- newChoiceBox(FXCollections.observableArrayList(
- "To:","Cc:", "Bcc:")
- );
- sendTo.setPrefWidth(100);
- GridPane.setConstraints(sendTo, 0, 0);
- grid.getChildren().add(sendTo);
- final TextField tbTo = newTextField();
- tbTo.setPrefWidth(400);
- GridPane.setConstraints(tbTo,1, 0);
- grid.getChildren().add(tbTo);
- final Label subjectLabel =new Label("Subject:");
- GridPane.setConstraints(subjectLabel, 0, 1);
- grid.getChildren().add(subjectLabel);
- final TextField tbSubject =new TextField();
- tbTo.setPrefWidth(400);
- GridPane.setConstraints(tbSubject, 1, 1);
- grid.getChildren().add(tbSubject);
- root.getChildren().add(grid);
- final HTMLEditor htmlEditor =new HTMLEditor();
- htmlEditor.setPrefHeight(370);
- root.getChildren().addAll(htmlEditor, newButton("Send"));
- final Label htmlLabel = newLabel();
- htmlLabel.setWrapText(true);
- scene.setRoot(root);
- stage.setScene(scene);
- stage.show();
- }
- public static void main(String[]args) {
- launch(args);
- }
- }
用戶界面包含一個選擇框來選擇收件人的類型,兩個文本框用於錄入郵件地址和主題信息,標籤用於標識主題字段,還包含了編輯器和發送按鈕。
UI控件通過Grid和VBox安排在程序的場景中。
圖19-5 郵件客戶端界面
可以通過調用setPrefWidth和setPrefHeight方法爲HTMLEditor對象指定高和寬。也可以保持默認的高和寬。例19-4指定了組件的高。它的寬度通過Vbox容器來定義。當文本內容超過編輯區的高度時,垂直滾動條會出現。
獲得HTML內容
通過JavaFX的HTMLEditor控件,你可以編輯文本並設定和初始內容。另外,你可以獲得通過HTML格式獲得鍵入和編輯的內容。例19-5所示的程序實現了這個任務。
- import javafx.application.Application;
- import javafx.event.ActionEvent;
- import javafx.event.EventHandler;
- import javafx.geometry.Insets;
- import javafx.geometry.Pos;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.*;
- import javafx.scene.layout.VBox;
- import javafx.scene.web.HTMLEditor;
- import javafx.stage.Stage;
- public class HTMLEditorSample extends Application {
- private final String INITIAL_TEXT= "Lorem ipsum dolor sit "
- + "amet, consecteturadipiscing elit. Nam tortor felis, pulvinar "
- + "in scelerisquecursus, pulvinar at ante. Nulla consequat"
- + "congue lectus insodales. Nullam eu est a felis ornare "
- + "bibendum et nectellus. Vivamus non metus tempus augue auctor "
- + "ornare. Duispulvinar justo ac purus adipiscing pulvinar. "
- + "Integer conguefaucibus dapibus. Integer id nisl ut elit "
- + "aliquam sagittisgravida eu dolor. Etiam sit amet ipsum "
- + "sem.";
- @Override
- public void start(Stage stage) {
- stage.setTitle("HTMLEditor Sample");
- stage.setWidth(500);
- stage.setHeight(500);
- Scene scene = new Scene(newGroup());
- VBox root = new VBox();
- root.setPadding(new Insets(8,8, 8, 8));
- root.setSpacing(5);
- root.setAlignment(Pos.BOTTOM_LEFT);
- final HTMLEditor htmlEditor =new HTMLEditor();
- htmlEditor.setPrefHeight(245);
- htmlEditor.setHtmlText(INITIAL_TEXT);
- final TextArea htmlCode = newTextArea();
- htmlCode.setWrapText(true);
- ScrollPane scrollPane = newScrollPane();
- scrollPane.getStyleClass().add("noborder-scroll-pane");
- scrollPane.setContent(htmlCode);
- scrollPane.setFitToWidth(true);
- scrollPane.setPrefHeight(180);
- Button showHTMLButton = newButton("Produce HTML Code");
- root.setAlignment(Pos.CENTER);
- showHTMLButton.setOnAction(new EventHandler<ActionEvent>() {
- @Override public voidhandle(ActionEvent arg0) {
- htmlCode.setText(htmlEditor.getHtmlText());
- }
- });
- root.getChildren().addAll(htmlEditor, showHTMLButton, scrollPane);
- scene.setRoot(root);
- stage.setScene(scene);
- stage.show();
- }
- public static void main(String[]args) {
- launch(args);
- }
- }
HTMLEditor對象調用getHTMLText方法將編輯的內容和表現分離爲HTML字串。這個信息發送至文本區,以便能觀察、複製、粘貼產生的HTML代碼。圖19-6展示了在HTMLEditor編輯器中編輯文本的例子。
圖19-6 獲得HTML內容
同樣,你可以獲取HTML代碼並保存在文件或發送到WebView對象以同步編輯器和嵌入的瀏覽器。例19-6展示瞭如何實現這個任務。
例19-6 在瀏覽器中繪製編輯的HTML內容
- import javafx.application.Application;
- import javafx.event.ActionEvent;
- import javafx.event.EventHandler;
- import javafx.geometry.Insets;
- import javafx.geometry.Pos;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.*;
- import javafx.scene.layout.VBox;
- import javafx.scene.web.HTMLEditor;
- import javafx.scene.web.WebEngine;
- import javafx.scene.web.WebView;
- import javafx.stage.Stage;
- public class HTMLEditorSample extends Application {
- private final String INITIAL_TEXT= "Lorem ipsum dolor sit "
- + "amet, consecteturadipiscing elit. Nam tortor felis, pulvinar "
- + "in scelerisquecursus, pulvinar at ante. Nulla consequat"
- + "congue lectus insodales. Nullam eu est a felis ornare "
- + "bibendum et nectellus. Vivamus non metus tempus augue auctor "
- + "ornare. Duispulvinar justo ac purus adipiscing pulvinar. "
- + "Integer conguefaucibus dapibus. Integer id nisl ut elit "
- + "aliquam sagittisgravida eu dolor. Etiam sit amet ipsum "
- + "sem.";
- @Override
- public void start(Stage stage) {
- stage.setTitle("HTMLEditor Sample");
- stage.setWidth(500);
- stage.setHeight(500);
- Scene scene = new Scene(newGroup());
- VBox root = new VBox();
- root.setPadding(new Insets(8,8, 8, 8));
- root.setSpacing(5);
- root.setAlignment(Pos.BOTTOM_LEFT);
- final HTMLEditor htmlEditor =new HTMLEditor();
- htmlEditor.setPrefHeight(245);
- htmlEditor.setHtmlText(INITIAL_TEXT);
- final WebView browser = newWebView();
- final WebEngine webEngine =browser.getEngine();
- ScrollPane scrollPane = newScrollPane();
- scrollPane.getStyleClass().add("noborder-scroll-pane");
- scrollPane.setStyle("-fx-background-color: white");
- scrollPane.setContent(browser);
- scrollPane.setFitToWidth(true);
- scrollPane.setPrefHeight(180);
- Button showHTMLButton = newButton("Load Content in Browser");
- root.setAlignment(Pos.CENTER);
- showHTMLButton.setOnAction(new EventHandler<ActionEvent>() {
- @Override public void handle(ActionEventarg0) {
- webEngine.loadContent(htmlEditor.getHtmlText());
- }
- });
- root.getChildren().addAll(htmlEditor, showHTMLButton, scrollPane);
- scene.setRoot(root);
- stage.setScene(scene);
- stage.show();
- }
- public static void main(String[]args) {
- launch(args);
- }
- }
從htmlEditor組件接收的HTML代碼被加載到爲嵌入的瀏覽器指定內容的WebEngine對象中,每次當用戶單擊“Load Content in Browser”按鈕時,編輯內容在瀏覽器中得到更新。圖19-7演示了這個動作。
圖19-7 在瀏覽器中加載內容
你可以使用Text組件來添加非編輯內容,參閱“在JavaFX中使用Text和Text效果”獲得更多關於Text組件的信息。
相關API
- HTMLEditor
- WebView
- WebEngine
- Label
- Button
- TextField
- ChoiceBox
- ScrollPane
20 標題面板和可摺疊面板
標題面板是一個帶標題的面板,它可以打開或關閉,還可以封裝任何的結點,比如UI控件和圖像,以及增加到佈局容器中的UI元素組。
標題面板可以通過摺疊控件分組,以更可以創建多個面板並一次顯示出來。圖20-1展示了一個包含三個標題面板的摺疊控件。
圖20-1 有三個標題面板的摺疊面板
使用JavaFX API中的Accordion類和TitlePane類可在你的應用程序中實現這些控件。
創建標題面板
創建一個標題控件需定義一個標題和一些內容。你可以通過使用TitlePane類的兩參數構造器來創建,或者使用setText和setContent方法。兩種方法均在例20-1中體現了。
例20-1 聲明一個標題面板對象
- //使用兩參數構造器
- TitledPane tp = new TitledPane("My Titled Pane", newButton("Button"));
- //使用方法
- TitledPane tp = new TitledPane();
- tp.setText("My Titled Pane");
- tp.setContent(new Button("Button"));
編譯和運行上述兩種方法之一,均會生成圖20-2所示的控件。
圖20-2 有按鈕的標題面板
標題面板自動調整尺寸以容納這些內容的尺寸。你可以增加多行文本來測試在圖20-3中的結果。
圖20-3 帶文本的標題面板
不需要明確地設定標題面板的最小、最大或最佳尺寸,因爲這些方法會在面板打開或關閉時帶來不可預期的行爲。
例20-2中所示的代碼段通過將它們集成到GridPane中的方式在標題面板中增加了幾個控件。
例20-2 帶GridPane佈局容器的標題面板
- TitledPane gridTitlePane = new TitledPane();
- GridPane grid = new GridPane();
- grid.setVgap(4);
- grid.setPadding(new Insets(5, 5, 5, 5));
- grid.add(new Label("First Name: "), 0, 0);
- grid.add(new TextField(), 1, 0);
- grid.add(new Label("Last Name: "), 0, 1);
- grid.add(new TextField(), 1, 1);
- grid.add(new Label("Email: "), 0, 2);
- grid.add(new TextField(), 1, 2);
- gridTitlePane.setText("Grid");
- gridTitlePane.setContent(grid);
編譯並運行上述代碼,輸出如圖20-4所示:
圖20-4 包含幾個控件的標題面板
你可以定義標題面板打開或關閉的方式。默認地,標題面板是可收縮並且會動態地移動。如果低迷的應用程序禁止關閉標題面板,可向setCollalsible方法傳遞一個false參數。例20-3中的代碼實現了此任務。
例20-3 調整標題面板的樣式
- TitledPane tp = new TitledPane();
- //prohibit closing
- tp.setCollapsible(false);
- //prohibit animating
- tp.setAnimated(false);
將標題面板增加到摺疊面板
在你的應用程序中,你可以將標題面板作爲獨立的元素,也可以通過使用Accordion控件將它們集成到一起。不需要明確地設定摺疊面板的最小、最大或最佳尺寸,因爲這些方法會在面板打開或關閉時帶來不可預期的行爲。
將標題面板增加到摺疊面板中與在按鈕組中增加按鈕類似。在摺疊面板中,同一時刻只能打開一個標題面板。例20-4創建了三個面板並把它們增加到了摺疊面板中。
例20-4 摺疊面板和三個標題面板
- import javafx.application.Application;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Accordion;
- import javafx.scene.control.TitledPane;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.paint.Color;
- import javafx.stage.Stage;
- public class TitledPaneSample extends Application {
- final String[] imageNames = newString[]{"Apples", "Flowers", "Leaves"};
- final Image[] images = newImage[imageNames.length];
- final ImageView[] pics = newImageView[imageNames.length];
- final TitledPane[] tps = newTitledPane[imageNames.length];
- public static void main(String[]args) {
- launch(args);
- }
- @Override public void start(Stagestage) {
- stage.setTitle("TitledPane");
- Scene scene = new Scene(newGroup(), 80, 180);
- scene.setFill(Color.GHOSTWHITE);
- final Accordion accordion =new Accordion ();
- for (int i = 0; i <imageNames.length; i++) {
- images[i] = new
- Image(getClass().getResourceAsStream(imageNames[i] + ".jpg"));
- pics[i] = newImageView(images[i]);
- tps[i] = newTitledPane(imageNames[i],pics[i]);
- }
- accordion.getPanes().addAll(tps);
- accordion.setExpandedPane(tps[0]);
- Group root =(Group)scene.getRoot();
- root.getChildren().add(accordion);
- stage.setScene(scene);
- stage.show();
- }
- }
三個標題面板在循環內創建,標題按鈕的內容作爲一個ImageView對象創建。標題面板通過使用getPanes和addAll方法增加到摺疊面板中,你可以使用add 方法替換addAll方法單獨地增加標題面板。
默認情況下,標題面板在程序開始時是收縮起來的。在運行例子中的程序時, setExpandedPane方法指定了裝有蘋果圖片的面板被打開。如圖20-5所示:
圖20-5 有三個標題面板的摺疊面板
爲摺疊面板處理事件
你可以在程序中使用標題面板和摺疊面板來表現不同的數據,例20-5創建了一個由GridPane佈局容器管理的獨立面板和用摺疊面板集成的三個標題面板。獨立的標題面板包含了一個email客戶端的UI元件,摺疊面板使得選擇的圖像出現在Grid標題面板的附件欄中。
例20-5 實現摺疊面板的ChangeListner
- import javafx.application.Application;
- import javafx.beans.value.ChangeListener;
- import javafx.beans.value.ObservableValue;
- import javafx.geometry.Insets;
- import javafx.scene.Group;
- import javafx.scene.Scene;
- import javafx.scene.control.Accordion;
- import javafx.scene.control.Label;
- import javafx.scene.control.TextField;
- import javafx.scene.control.TitledPane;
- import javafx.scene.image.Image;
- import javafx.scene.image.ImageView;
- import javafx.scene.layout.GridPane;
- import javafx.scene.layout.HBox;
- import javafx.scene.paint.Color;
- import javafx.stage.Stage;
- public class TitledPaneSample extends Application {
- final String[] imageNames = newString[]{"Apples", "Flowers", "Leaves"};
- final Image[] images = newImage[imageNames.length];
- final ImageView[] pics = newImageView[imageNames.length];
- final TitledPane[] tps = newTitledPane[imageNames.length];
- final Label label = newLabel("N/A");
- public static void main(String[]args) {
- launch(args);
- }
- @Override public void start(Stagestage) {
- stage.setTitle("TitledPane");
- Scene scene = new Scene(newGroup(), 800, 250);
- scene.setFill(Color.GHOSTWHITE);
- // --- GridPane container
- TitledPane gridTitlePane =new TitledPane();
- GridPane grid = newGridPane();
- grid.setVgap(4);
- grid.setPadding(new Insets(5,5, 5, 5));
- grid.add(new Label("To:"), 0, 0);
- grid.add(new TextField(), 1, 0);
- grid.add(new Label("Cc:"), 0, 1);
- grid.add(new TextField(), 1,1);
- grid.add(newLabel("Subject: "), 0, 2);
- grid.add(new TextField(), 1,2);
- grid.add(newLabel("Attachment: "), 0, 3);
- grid.add(label,1, 3);
- gridTitlePane.setText("Grid");
- gridTitlePane.setContent(grid);
- // --- Accordion
- final Accordion accordion =new Accordion ();
- for (int i = 0; i <imageNames.length; i++) {
- images[i] = new
- Image(getClass().getResourceAsStream(imageNames[i] + ".jpg"));
- pics[i] = newImageView(images[i]);
- tps[i] = newTitledPane(imageNames[i],pics[i]);
- }
- accordion.getPanes().addAll(tps);
- accordion.expandedPaneProperty().addListener(new
- ChangeListener<TitledPane>() {
- public voidchanged(ObservableValue<? extends TitledPane> ov,
- TitledPaneold_val, TitledPane new_val) {
- if (new_val!= null) {
- label.setText(accordion.getExpandedPane().getText() +
- ".jpg");
- }
- }
- });
- HBox hbox = new HBox(10);
- hbox.setPadding(newInsets(20, 0, 0, 20));
- hbox.getChildren().setAll(gridTitlePane, accordion);
- Group root =(Group)scene.getRoot();
- root.getChildren().add(hbox);
- stage.setScene(scene);
- stage.show();
- }
- }
當用戶在摺疊面板中打開標題面板時,摺疊面板的expandedPaneProperty屬性改變,ChangeListner對象發送這個變化,然後摺疊面板中展開的標題面板用來構造捕獲的文件名。這個文件名作爲附件標籤對象的文本。
圖20-6展示了當程序開始後的樣式,捕獲的標籤包含“N/A”,因爲還沒有標題面板被選中。
圖20-6 標題面板的初始視圖
如果你展開了葉子標題面板,捕獲標籤將包含“Leaves.jpg”,如圖20-7所示。
圖20-7 葉子標題面板打開時的標題面板的例子
由於TitlePane和Accordion類都屬於Node類的子類,你可以對它使用可視化特效或變換。也可以使用CSS樣式來改變控件的外觀。
相關API
- TitlePane
- Acoordion
- Label
- GridPane
- TextField