天天看點

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

JavaFX Scene Builder 2.0工具下載下傳:需要在Oracle官網下載下傳

JavaFX Scene Builder可以拖拽控件生成一個fxml檔案(其實本質是個xml檔案),避免大量Java代碼去實作布局。

在JavaFX Scene Builder可以清晰看到控件的一個樹狀結構,這比寫大量的Java代碼顯得邏輯清楚。

本人一般拖拽一個然後再去直接修改fxml,直接在代碼裡面改一些東西或者直接去粘貼一些代碼,這樣感覺效率高點,以後想修改直接在工具打開,結合fxml代碼去修改布局

先看一個使用Java代碼實作的一個JavaFx程式,感覺閱讀有點難受,這是本人沒使用JavaFX Scene Builder 2.0的一個純靠Java代碼去實作界面的例子。

package jfxapp;

import java.util.Iterator;

import javax.swing.JFrame;

import javafx.scene.control.MenuItem;

import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FxAppV2 extends Application {

	public static void main(String[] args) {
		launch(args);

	}

	//頂部菜單欄
	public Parent initHBoxMenu() {
		// 第2層,第一個HBox
				HBox hBoxMenu = new HBox();
				// 第3層,
				MenuBar menuBar = new MenuBar();
				Menu menuFile = new Menu("檔案");
				Menu menuEdit = new Menu("編輯");
				Menu menuWindow = new Menu("視窗");
				Menu menuSetting = new Menu("設定");
				// 添加到MenuBar
				menuBar.getMenus().add(menuFile);
				menuBar.getMenus().add(menuEdit);
				menuBar.getMenus().add(menuWindow);
				menuBar.getMenus().add(menuSetting);

				MenuItem menuItem = new MenuItem("建立");
				menuFile.getItems().add(menuItem);
				HBox.setHgrow(menuBar, Priority.ALWAYS);
				
				hBoxMenu.getChildren().addAll(menuBar);
				return hBoxMenu;
	}
	//初始化TabPane
	public Parent initTabPane() {
				// hBoxConSpTabPane
				TabPane hBoxConSpTabPane = new TabPane();
				Tab tab = new Tab();
				tab.setText("文檔編輯");
				TextArea textArea = new TextArea();
				tab.setContent(textArea);
				tab.setTooltip(new Tooltip("提示"));
//				tab.setGraphic(imageView);
				hBoxConSpTabPane.getTabs().add(tab);
				// 放置位置
				hBoxConSpTabPane.setSide(Side.TOP);
				hBoxConSpTabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.SELECTED_TAB);
				return hBoxConSpTabPane;
	}
	public Parent initVBox() {
		// hBoxConSpVBox
				VBox hBoxConSpVBox = new VBox();
				
				Label label1 = new Label("TreeView");
				Label label2= new Label("ListView");
				ListView<String> listView = new ListView<>();
				ObservableList<String> observableList = FXCollections.observableArrayList("123", "abc", "abc", "abc", "abc",
						"abc", "abc", "abc", "你好啊");
				listView.setItems(observableList);
				TreeView<String> treeView = new TreeView<>();
				TreeItem<String> item = new TreeItem<>("根節點");
		        treeView.setRoot(item);
		        item.setExpanded(false);//是否是展開狀态

		        TreeItem<String> i1 = new TreeItem<>("電影");
		        TreeItem<String> i2 = new TreeItem<>("音樂");
		        TreeItem<String> i3 = new TreeItem<>("遊戲");
		        //添加節點
		        item.getChildren().addAll(i1,i2,i3);

		        TreeItem<String> i4 = new TreeItem<>("蕩寇風雲");
		        TreeItem<String> i5 = new TreeItem<>("變形金剛5");
		        i1.setExpanded(false);
		        //添加節點
		        i1.getChildren().addAll(i4,i5);
				
				hBoxConSpVBox.setPadding(new Insets(9, 9, 9, 8));
				hBoxConSpVBox.setSpacing(9);// 設定垂直間隔距離
				// 設定當視窗變大變寬時,ListView随之高度寬度也發生改變
				VBox.setVgrow(listView, Priority.ALWAYS);
				hBoxConSpVBox.getChildren().addAll(label1,treeView,label2,listView);
				
				return hBoxConSpVBox;

	}
	
	public Parent vSplitPane() {
		 SplitPane hBoxContVSplitPane = new SplitPane();
		 hBoxContVSplitPane.setOrientation(Orientation.VERTICAL);
		// 主内容SplitPane
				SplitPane hBoxConHSplitPane = new SplitPane();
				// 水準分隔
				hBoxConHSplitPane.setOrientation(Orientation.HORIZONTAL);

				Parent p1 = initVBox();
				Parent p2 =initTabPane();
				// 添加内容 左右分别是listView和 scrollPane()
				hBoxConHSplitPane.getItems().addAll(p1, p2);

				hBoxConHSplitPane.setResizableWithParent(p2, true);
				hBoxConHSplitPane.setResizableWithParent(p1, false);
				hBoxConHSplitPane.setDividerPositions(0.2f, 0.8, 1.0f);

				// 列印台SplitPane
				SplitPane hBoxConsoleSplitPane = new SplitPane();
				// 水準分隔
				hBoxConsoleSplitPane.setOrientation(Orientation.HORIZONTAL);
				ScrollPane scrollPane = new ScrollPane();
				scrollPane.setContent(new TextArea());
				scrollPane.setFitToHeight(true);
				scrollPane.setFitToWidth(true);
				HBox buttonBox = new HBox();
				Button btn1 = new Button("複制");
				Button btn2 = new Button("複制");
				Button btn3 = new Button("複制");
				buttonBox.getChildren().addAll(btn1, btn2, btn3);
				// 列印台添加
				hBoxConsoleSplitPane.getItems().addAll(buttonBox, scrollPane);
				hBoxConsoleSplitPane.setDividerPositions(0.2f, 1.0f);
				hBoxConsoleSplitPane.setResizableWithParent(scrollPane, true);
				hBoxConsoleSplitPane.setResizableWithParent(buttonBox, false);
				// 上下SplitPane
				hBoxContVSplitPane.getItems().addAll(hBoxConHSplitPane, hBoxConsoleSplitPane);
				hBoxContVSplitPane.setDividerPositions(0.8f, 1.0f);
				hBoxContVSplitPane.setResizableWithParent(hBoxConHSplitPane, true);
				hBoxContVSplitPane.setResizableWithParent(hBoxConsoleSplitPane, false);
				
				return hBoxContVSplitPane;
	}
	public Parent initHBoxContent() {
		HBox hBoxContent = new HBox();
		Parent  p = vSplitPane();
		hBoxContent.getChildren().addAll(p);
		// 使hBoxConSplitPane可随視窗高寬變化而變化
		HBox.setHgrow(p, Priority.ALWAYS);
		return hBoxContent;
	}
	
	public Parent initHBoxFoot() {
		HBox hBoxFoot = new HBox();
		hBoxFoot.getChildren().addAll(new Label("底部"));
		return hBoxFoot;
	}
	public Parent initView() {

		// 第一層,最外層
		VBox vBoxParent = new VBox();

		vBoxParent.setPadding(new Insets(9, 9, 9, 8));
		vBoxParent.setSpacing(9);// 設定垂直間隔距離

		Parent  p = initHBoxContent();
		// 第一層添加内容
		vBoxParent.getChildren().addAll(initHBoxMenu(),p,initHBoxFoot());
		// 使hBoxContent可随視窗高寬變化而變化
		VBox.setVgrow(p, Priority.ALWAYS);

		return vBoxParent;

	}

	@Override
	public void start(Stage primaryStage) throws Exception {

		primaryStage.setTitle("編輯器");

		Scene scene = new Scene(initView(), 900, 700); // 建立一個場景
		// 第3步
		// 設定舞台的場景
		primaryStage.setScene(scene);
//					primaryStage.setResizable(false); // 設定舞台的尺寸是否允許變化
		// 顯示舞台。相當于JFrame的setVisible(true)
		primaryStage.show();// 顯示stage

	}

}

           

運作起來後是這樣的

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

感覺也能實作布局,但是想去當去修改某個地方,或者實作更複雜布局,恐怕看着上面代碼有點頭皮發麻吧哈哈,建議使用工具

下面先通過一個簡單執行個體說明

這是一段fxm的裡面的代碼(後面有完整代碼)

<AnchorPane prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
         <children>
            <Label layoutX="28.0" layoutY="22.0" text="Label" />
            <TextArea layoutX="28.0" layoutY="62.0" prefHeight="248.0" prefWidth="555.0" />
            <Button layoutX="48.0" layoutY="325.0" mnemonicParsing="false" text="Button" />
            <Button layoutX="169.0" layoutY="325.0" mnemonicParsing="false" text="Button" />
            <Button layoutX="279.0" layoutY="325.0" mnemonicParsing="false" text="Button" />
         </children></AnchorPane>
           

fxml在JavaFX Scene Builder 2.0打開後可以看到這樣一個樹狀結構,感覺還是比較直覺的

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

對應是這樣實體圖

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

fxml完整xml代碼如下

<?xml version="1.0" encoding="UTF-8"?>


<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <AnchorPane prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
         <children>
            <Label layoutX="28.0" layoutY="22.0" text="Label" />
            <TextArea layoutX="28.0" layoutY="62.0" prefHeight="248.0" prefWidth="555.0" />
            <Button layoutX="48.0" layoutY="325.0" mnemonicParsing="false" text="Button" />
            <Button layoutX="169.0" layoutY="325.0" mnemonicParsing="false" text="Button" />
            <Button layoutX="279.0" layoutY="325.0" mnemonicParsing="false" text="Button" />
         </children></AnchorPane>
   </children>
</VBox>

           
說一下,上面其實最外層容器就是一個VBox,在VBox裡面放了一個AnchorPane,VBox.vgrow=“ALWAYS” 表示這個AnchorPane總是随Vbox變化而自适應(增大),這個AnchorPane裡面有Label、TextArea和3個Button。然後就是設定布局的位置和大小等等。

下面看一個更複雜的執行個體

實體圖

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

樹狀結構圖

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

Home.fxml

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>

<VBox fx:id="mainbox" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sugar.base2.controller.HomeController">
   <children>
       <HBox fx:id="opBox" prefHeight="45.0" prefWidth="200.0" VBox.vgrow="NEVER">
         <children>
            <AnchorPane fx:id="opAp" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS">
               <children>
                  <Button fx:id="closeStageBtn" mnemonicParsing="false" prefWidth="80" text="關閉" AnchorPane.rightAnchor="2" AnchorPane.topAnchor="6" />
                   <Button fx:id="switchStageBtn" mnemonicParsing="false" prefWidth="80" text="視窗切換" AnchorPane.rightAnchor="120" AnchorPane.topAnchor="6" />
               </children></AnchorPane>
         </children></HBox>
      <HBox prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
         <children>
            <Accordion HBox.hgrow="ALWAYS">
              <panes>
                <TitledPane animated="false" text="工具欄1">
                     <content>
                         <GridPane fx:id="topGridPane">
                                <columnConstraints>
                                  <ColumnConstraints hgrow="NEVER" minWidth="10.0" prefWidth="100.0" />
                                  <ColumnConstraints hgrow="NEVER" minWidth="10.0" prefWidth="100.0" />
                                </columnConstraints>
                                <rowConstraints>
                                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="NEVER" />
                                </rowConstraints>
                              </GridPane>
                     </content>
                  </TitledPane>
                <TitledPane animated="false" text="工具類2" />
              </panes>
            </Accordion>
         </children>
      </HBox>
      <HBox prefHeight="322.0" prefWidth="600.0" VBox.vgrow="ALWAYS">
         <children>
            <SplitPane fx:id="rightSplitPane" dividerPositions="0.26" prefHeight="300.0" prefWidth="603.0" HBox.hgrow="ALWAYS">
              <items>
                <AnchorPane fx:id="leftAnchorPane" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" SplitPane.resizableWithParent="false">
                     <children>
                        <ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="298.0" prefWidth="200.0" AnchorPane.bottomAnchor="2" AnchorPane.leftAnchor="2" AnchorPane.rightAnchor="2" AnchorPane.topAnchor="2">
                           <content>
                              <GridPane fx:id="leftGridPane" prefHeight="265.0" prefWidth="200.0">
                                <columnConstraints>
                                  <ColumnConstraints hgrow="NEVER" minWidth="120.0" prefWidth="120.0" />
                                </columnConstraints>
                                <rowConstraints>
                                  <RowConstraints minHeight="120.0" prefHeight="200.0" vgrow="NEVER" />
                                  <RowConstraints minHeight="120.0" prefHeight="200.0" vgrow="NEVER" />
                                  <RowConstraints minHeight="120.0" prefHeight="200.0" vgrow="NEVER" />
                                    <RowConstraints minHeight="120.0" prefHeight="200.0" vgrow="NEVER" />
                                    <RowConstraints minHeight="120.0" prefHeight="200.0" vgrow="NEVER" />
                                    <RowConstraints minHeight="120.0" prefHeight="200.0" vgrow="NEVER" />
                                </rowConstraints>
                                 <children>
                                    <AnchorPane fx:id="leftAp1" prefHeight="120.0" prefWidth="120.0" GridPane.rowIndex="0">
                                       <children>
<!--                                          <ImageView fx:id="leftIv1" AnchorPane.topAnchor="3" AnchorPane.bottomAnchor="3" AnchorPane.leftAnchor="3" AnchorPane.rightAnchor="3"/>-->
<!--                                          <Label text="Label" />-->
                                       </children>
                                    </AnchorPane>
                                     <AnchorPane fx:id="leftAp2" prefHeight="120.0" prefWidth="120.0" GridPane.rowIndex="1" />
                                     <AnchorPane fx:id="leftAp3" prefHeight="120.0" prefWidth="120.0" GridPane.rowIndex="2" />
                                     <AnchorPane fx:id="leftAp4" prefHeight="120.0" prefWidth="120.0" GridPane.rowIndex="3" />
                                     <AnchorPane fx:id="leftAp5" prefHeight="120.0" prefWidth="120.0" GridPane.rowIndex="4" />
                                     <AnchorPane fx:id="leftAp6" prefHeight="120.0" prefWidth="120.0" GridPane.rowIndex="5" />
                                 </children>
                              </GridPane>
                           </content>
                        </ScrollPane>
                     </children>
                  </AnchorPane>
                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" SplitPane.resizableWithParent="true">
                     <children>
                        <ScrollPane fitToHeight="true" fitToWidth="true" prefViewportHeight="271.0" prefViewportWidth="200.0" AnchorPane.bottomAnchor="2" AnchorPane.leftAnchor="2" AnchorPane.rightAnchor="2" AnchorPane.topAnchor="2">
                           <content>
                              <GridPane fx:id="rightGridPane" layoutX="108.0" layoutY="10.0" prefHeight="271.0" prefWidth="200.0">
                                  <columnConstraints>
                                      <ColumnConstraints hgrow="NEVER" minWidth="180.0" prefWidth="180.0" />
                                      <ColumnConstraints hgrow="NEVER" minWidth="180.0" prefWidth="180.0" />
                                      <ColumnConstraints hgrow="NEVER" minWidth="180.0" prefWidth="180.0" />
                                      <ColumnConstraints hgrow="NEVER" minWidth="180.0" prefWidth="180.0" />
                                  </columnConstraints>
                                  <rowConstraints>
                                      <!--  動态添加行數 建議放置一個RowConstraints模闆在這裡!!!!!-->
                                      <RowConstraints minHeight="180.0" prefHeight="180.0" vgrow="NEVER" />
                                  </rowConstraints>
                              </GridPane>
                           </content>
                        </ScrollPane>
                     </children>
                  </AnchorPane>
              </items>
            </SplitPane>
         </children>
      </HBox>

   </children>
</VBox>

           

注意 fx:controller=“sugar.base2.controller.HomeController”

對應的是一個實作Initializable接口的類,本篇後面有代碼。

這個類可以沒有,就像上面之前的fxml 那麼fx:controller="sugar.base2.controller.HomeController"就必須去掉。

controller的作用就是這個fxml被加載

//加載fxml的java代碼

Parent p = ResourcesUtils.getFxmlFile(“Home.fxml”);

這個HomeController裡面實作接口的

@Override

public void initialize(URL location, ResourceBundle resources) {

}

方法就會執行

對應的Controller

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml
HomeController
package sugar.base2.controller;


import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import sugar.base2.handler.DragWindowHandler;
import sugar.base2.onlyOp.OnlyReadRes;
import sugar.base2.operate.OpeFlag;
import sugar.base2.windows.SearchStage;
import sugar.utils.fxapp.GridPaneUtils;
import sugar.utils.fxapp.ResourcesUtils;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

public class HomeController implements Initializable {

    @FXML
    HBox opBox;
    @FXML
    AnchorPane opAp;
    @FXML
    Button closeStageBtn;
    @FXML
    Button switchStageBtn;
    @FXML
    GridPane topGridPane;
    @FXML
    AnchorPane leftAnchorPane;
    @FXML
    GridPane leftGridPane;
    @FXML
    GridPane rightGridPane;
    @FXML
    SplitPane rightSplitPane;
    @FXML
    AnchorPane leftAp1;
    @FXML
    AnchorPane leftAp2;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        //List<String> list = DataOpe.ok().getData("images1");
        //當資料量大時,可能等待時間較長,應該加入一個緩存,第一次打開緩存進去
        Label label = new Label("菜單欄");
        CheckBox checkBox = new CheckBox();
        checkBox.setText("顯示");
        //topGridPane.addRow(0,iv);
         topGridPane.addRow(0,label,checkBox);


         //預設設定沒有選中
        if (!checkBox.isSelected()){
            leftAnchorPane.setVisible(false);
            rightSplitPane.getItems().remove(0);
            rightSplitPane.setDividerPositions(0.0);
        }
         Node left = leftAnchorPane;
       //if else放在事件設定外隻會執行一次!!!!!!!
        checkBox.setOnAction(event -> {
            //如果之前狀态被選中,再次點選就是未必選中!!!!
            if (checkBox.isSelected()){
                leftAnchorPane.setVisible(true);
                rightSplitPane.getItems().add(0,leftAnchorPane);
                rightSplitPane.setDividerPositions(0.26);
                //rightSplitPane.getDividers().add(new SplitPane.Divider());
            }else if (!checkBox.isSelected()){
                leftAnchorPane.setVisible(false);
                rightSplitPane.getItems().remove(0);
                rightSplitPane.setDividerPositions(0.0);
            }
        });

        ImageView leftIv1 = new ImageView(new Image(ResourcesUtils.getJarFilePath("home/tools_256px.png")));
        leftIv1.setFitHeight(120);
        leftIv1.setFitWidth(120);
        leftAp1.getChildren().addAll(leftIv1,new Label("aan"));

        ImageView leftIv2 = new ImageView(new Image(ResourcesUtils.
                getJarFilePath("home/tools_256px.png")));
        leftIv2.setFitHeight(120);
        leftIv2.setFitWidth(120);
        leftAp2.getChildren().addAll(leftIv2,new Label("aan"));

        List<String> list = new ArrayList<>();
//        for (int i = 0 ;i< pic.length;i++){
//            list.add(pic[i]);
//        }
        //當資料量大時,可能等待時間較長,應該加入一個緩存,第一次打開緩存進去
        List<ImageView> imageViews = new ArrayList<>();
        for (int i = 0; i < OnlyReadRes.rightPics.length ; i++){
            Image image =new Image(ResourcesUtils
                    .getJarFilePath(OnlyReadRes.rightPics[i]));
            ImageView iv = new ImageView(image);
            iv.setFitWidth(120);
            iv.setFitHeight(120);
            imageViews.add(iv);
            iv.setOnMouseEntered(event1 -> {
            });
        }

        GridPaneUtils.setImagsToPane2(rightGridPane,imageViews);
        //第一個按鈕的事件
        imageViews.get(0).setOnMouseClicked(event -> {
            Parent p = ResourcesUtils.getFxmlFile(OnlyReadRes.picFxml);
            SearchStage searchStage = new SearchStage(p,"");
            searchStage.show();

        });



        opBox.setOnMouseDragEntered(event -> {
            System.out.println("開始拖動");
        });

        //------------視窗頂部設定拖動
        EventHandler handler = new DragWindowHandler(OpeFlag.ok().getStage());//primaryStage為start方法中的局部b
        opBox.setOnMousePressed(handler);//如果去掉這一行代碼将會使滑鼠進入面闆時面闆左上角會定位到滑鼠的位置
        opBox.setOnMouseDragged(handler);

        //-----------設定頂部背景
        opAp.setBackground(new Background(new BackgroundImage(new Image(ResourcesUtils.getJarFilePath("imgs/bar.JPG")),null
        ,null,null,null)));

        closeStageBtn.setOnAction(event -> {
            if (OpeFlag.ok().getStage().isShowing()){
                OpeFlag.ok().getStage().close();
            }

        });

        switchStageBtn.setOnAction(event -> {
            if (OpeFlag.ok().getStage().isFullScreen()){
                OpeFlag.ok().getStage().setFullScreen(false);
            }else {
                OpeFlag.ok().getStage().setFullScreen(true);
            }
            //if ()
        });

        //因為使用自定義頂部欄
        //需要自己實作視窗拖動改變大小


    }
}

           
FxApp,繼承 Application,這是JavaFx啟動程式。我這裡stage.initStyle(StageStyle.TRANSPARENT);視窗沒有标題欄,自然就沒有關閉、最小化最大化按鈕
package sugar.base2;

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import sugar.base2.onlyOp.OnlyReadRes;
import sugar.base2.operate.OpeFlag;
import sugar.utils.fxapp.ResourcesUtils;

import java.util.concurrent.CompletableFuture;

public class FxApp extends Application {
    public static void main(String[] args) throws InterruptedException {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Stage stage = new Stage();
        OpeFlag.init().setStage(stage);
//        ViewOpe.getInstance();
//        DataOpe.getInstance();
        //加載fxml
        Parent p = ResourcesUtils.getFxmlFile(OnlyReadRes.startFxml);
        Scene scene = new Scene(p, OnlyReadRes.sceneWidth, OnlyReadRes.sceneHeight);
        stage.setScene(scene);
//		primaryStage.initStyle(StageStyle.DECORATED);//正常顯示 也是預設
//		primaryStage.initStyle(StageStyle.TRANSPARENT);//三個組鍵都沒有(最小化 最大化 關閉)
//		primaryStage.initStyle(StageStyle.UNDECORATED);//背景透明
//		primaryStage.initStyle(StageStyle.UNIFIED);//無title欄的背景顔色
//		primaryStage.initStyle(StageStyle.UTILITY);//無最小化最大化 隻有關閉按鈕
        stage.setTitle(OnlyReadRes.stageTitle);
        stage.initStyle(StageStyle.TRANSPARENT);

        stage.show();
        System.out.println(" ==stage.getX()=="+ stage.getX()+"==stage.getY()=="+stage.getY());
        
        if (stage.isShowing()){

//            CompletableFuture.runAsync(() -> {
//                System.out.println("Stage已初始化!!");
//            });

        }

    
        stage.setOnHidden(event -> {
            System.out.println("stage隐藏");
        });
       stage.setOnShowing(event -> {
            System.out.println("stage顯示");
       });
       stage.setOnCloseRequest(event -> {

       });
       //設定為空字元串時全屏就沒有提示!!
       stage.setFullScreenExitHint("");
      // stage.set

        //OnlyReadService.pools.submit();


    }
}

           

運作看一下,感覺一般,還可以加css美化

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml
JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml

前面說了沒有标題欄,自然也無法拖動,解決方法

回看之前的HomeController這段代碼
//------------視窗頂部設定拖動
        EventHandler handler = new DragWindowHandler(OpeFlag.ok().getStage());
        opBox.setOnMousePressed(handler);//如果去掉這一行代碼将會使滑鼠進入面闆時面闆左上角會定位到滑鼠的位置
        opBox.setOnMouseDragged(handler);
           

視窗拖動處理Handler

JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml
package sugar.base2.handler;

import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

public class DragWindowHandler implements EventHandler<MouseEvent> {

    private Stage primaryStage;//primaryStage為start方法頭中的Stage
    private double oldStageX;
    private double oldStageY;
    private double oldScreenX;
    private double oldScreenY;

    public DragWindowHandler(Stage primaryStage) {//構造器
        this.primaryStage = primaryStage;
    }

    @Override
    public void handle(MouseEvent e) {
        if (e.getEventType() == MouseEvent.MOUSE_PRESSED) {    //滑鼠按下的事件
            this.oldStageX = this.primaryStage.getX(); //stage X坐标 最左上角X坐标
            this.oldStageY = this.primaryStage.getY(); //stage Y坐标 最左上角Y坐标
            this.oldScreenX = e.getScreenX(); // 滑鼠按下在螢幕的 X坐标
            this.oldScreenY = e.getScreenY(); // 滑鼠按下在螢幕的 Y坐标
            System.out.println("==StageX----------- MOUSE_PRESSED==");
            System.out.println("==stage X坐标=="+this.oldStageX);
            System.out.println("==stage Y坐标=="+this.oldStageY);
            System.out.println("==ScreenX=="+ this.oldScreenX);
            System.out.println("==ScreenY=="+ this.oldScreenY);
        } else if (e.getEventType() == MouseEvent.MOUSE_DRAGGED) {  //滑鼠拖動的事件
            //拖動時stage X坐标 = 滑鼠目前 X坐标 - 之前滑鼠按下在螢幕的 X坐标 + stage X坐标
            //可能這不怎麼明顯,加一個括号,
            //            當括号的值是正的,表示右移;負的,表示左移
            //拖動時stage X坐标 = (滑鼠目前 X坐标 - 之前滑鼠按下在螢幕的 X坐标) + stage X坐标

            //x的位置
            this.primaryStage.setX(e.getScreenX() - this.oldScreenX + this.oldStageX);
            //y的位置
            this.primaryStage.setY(e.getScreenY() - this.oldScreenY + this.oldStageY);
        }
    }
}

           
注意之前Home.fxml這段代碼,其實這裡是用于動态生成一個 GridPane
<!--  動态添加行數 建議放置一個RowConstraints模闆在這裡!!!!!-->
                                      <RowConstraints minHeight="180.0" prefHeight="180.0" vgrow="NEVER" />
           
先看一下GridPaneUtils工具類
JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml
package sugar.utils.fxapp;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import sugar.utils.math.MathFormat;
import sugar.utils.math.MathUtils;

import java.util.List;

public class GridPaneUtils {

    public static void setNodeToPane(GridPane g, List<GridPane> views){
        Integer cols = g.getColumnConstraints().size(); //列數
        Integer rows = g.getRowConstraints().size(); //行數
        int length = views.size();//ListView大小
        int count = cols; //行的個數
        int flag = 0; //目前周遊到的位置
        int position = 0; //顯示到哪一行
        RowConstraints exRow = null; //示例行數
        //如果有行數
        if (g.getRowConstraints().size()>0){
            exRow = g.getRowConstraints().get(0);
        }else {
            exRow.setVgrow(Priority.NEVER);
            exRow.setPrefHeight(200.0);
        }
        for (int v = 0 ; v < length ; v++){
            flag++;
            //如果可以整除表示此行數已顯示滿
            //加入每行顯示2個,那麼當flag=2+1=3 , 4+1=5, 7 , 9 , 11......換行
//            if (flag % count == 1){  //這是錯誤寫法
//                position++;
//            }
            // flag>cols 不是第一行且 (flag-1)整除行的個數等于0
            if (flag>cols &&((flag-1) % count == 0)){
                position++; //換行
                RowConstraints row = new RowConstraints();
                row.setPercentHeight(exRow.getPercentHeight());
                row.setVgrow(exRow.getVgrow());
                g.getRowConstraints().add(position,row);
            }
            g.addRow(position,views.get(v));
        }
    }

    //設定ImageView自适應到GridPane
    public static void setImagsToPane2(GridPane g, List<ImageView> views){
        Integer cols = g.getColumnConstraints().size(); //列數
        Integer rows = g.getRowConstraints().size(); //行數
        int length = views.size();//ListView大小
        int count = cols; //行的個數
        int flag = 0; //目前周遊到的位置
        int position = 0; //顯示到哪一行
        RowConstraints exRow = null; //示例行數
        //如果有行數
        if (g.getRowConstraints().size()>0){
            exRow = g.getRowConstraints().get(0);
        }else {
            exRow.setVgrow(Priority.NEVER);
            exRow.setPrefHeight(200.0);
        }
        for (int v = 0 ; v < length ; v++){
            flag++;
            //如果可以整除表示此行數已顯示滿
            //加入每行顯示2個,那麼當flag=2+1=3 , 4+1=5, 7 , 9 , 11......換行
//            if (flag % count == 1){  //這是錯誤寫法
//                position++;
//            }
            // flag>cols 不是第一行且 (flag-1)整除行的個數等于0
            if (flag>cols &&((flag-1) % count == 0)){
                position++; //換行
                RowConstraints row = new RowConstraints();
                row.setPercentHeight(exRow.getPercentHeight());
                row.setVgrow(exRow.getVgrow());
                g.getRowConstraints().add(position,row);
            }
                g.addRow(position,views.get(v));
            }

    }


    //未成功實作,不再使用,可屏蔽這個靜态方法
    public static void setImagsToPane(GridPane g, List<ImageView> views){
        Integer cols = g.getColumnConstraints().size();
        Integer rows = g.getRowConstraints().size();

//        for (int r = 0;r < rows; r++) {
//            for (int c = 0; c < cols ;c++){
//            }
//        }
        int length = views.size();
        if (views!= null && length>0){
            //剛好小于等于一行的數目
            if (length<=cols){
                for (int v = 0 ; v < length ; v++){
                    g.addRow(0,views.get(v));
                }
            }
        }else {
            //
            int dotPosition = 0;
            String str =MathUtils.getMultiple(length,cols, MathFormat.f3); //擷取帶精度的
            for (int position=0;position<str.length();position++) {
                if (str.charAt(position)== MathFormat.dotFlag){
                    dotPosition = position;
                }
            }
            for(int position=0;position<dotPosition;position++){

            }
            //擷取行數
//            for (int v = 0 ; v < length ; v++){
//                g.addRow(0,views.get(v));
//            }
            for (int r = 0;r < rows; r++) {
              for (int c = 0; c < cols ;c++){

             }
          }
        }

    }

    public static List<Image> transformSize(List<Image> images,int d){
        for (Image v: images
             ) {
           double width = v.getWidth();
           double height = v.getHeight();
        }

        return images;
    }

}

           

其中這個靜态方法,設定ImageView自适應到GridPane

//設定ImageView自适應到GridPane
    public static void setImagsToPane2(GridPane g, List<ImageView> views){
        Integer cols = g.getColumnConstraints().size(); //列數
        Integer rows = g.getRowConstraints().size(); //行數
        int length = views.size();//ListView大小
        int count = cols; //行的個數
        int flag = 0; //目前周遊到的位置
        int position = 0; //顯示到哪一行
        RowConstraints exRow = null; //示例行數
        //如果有行數
        if (g.getRowConstraints().size()>0){
            exRow = g.getRowConstraints().get(0);
        }else {
            exRow.setVgrow(Priority.NEVER);
            exRow.setPrefHeight(200.0);
        }
        for (int v = 0 ; v < length ; v++){
            flag++;
            //如果可以整除表示此行數已顯示滿
            //加入每行顯示2個,那麼當flag=2+1=3 , 4+1=5, 7 , 9 , 11......換行
//            if (flag % count == 1){  //這是錯誤寫法
//                position++;
//            }
            // flag>cols 不是第一行且 (flag-1)整除行的個數等于0
            if (flag>cols &&((flag-1) % count == 0)){
                position++; //換行
                RowConstraints row = new RowConstraints();
                row.setPercentHeight(exRow.getPercentHeight());
                row.setVgrow(exRow.getVgrow());
                g.getRowConstraints().add(position,row);
            }
                g.addRow(position,views.get(v));
            }

    }
           
使用方法
//當資料量大時,可能等待時間較長,應該加入一個緩存,第一次打開緩存進去
        List<ImageView> imageViews = new ArrayList<>();
        for (int i = 0; i < OnlyReadRes.rightPics.length ; i++){
            Image image =new Image(ResourcesUtils
                    .getJarFilePath(OnlyReadRes.rightPics[i]));
            ImageView iv = new ImageView(image);
            iv.setFitWidth(120);
            iv.setFitHeight(120);
            imageViews.add(iv);
            iv.setOnMouseEntered(event1 -> {
            });
        }

        GridPaneUtils.setImagsToPane2(rightGridPane,imageViews);
           
其中 OnlyReadRes.rightPics我寫在一個final類
public static final String[] rightPics = {
            "home/main/openiphone_001.png",
            "home/main/openiphone_002.png",
            "home/main/openiphone_003.png",
            "home/main/openiphone_004.png",
            "home/main/openiphone_005.png",
            "home/main/openiphone_006.png",
            "home/main/openiphone_007.png",
            "home/main/openiphone_008.png",
            "home/main/openiphone_009.png",
            "home/main/openiphone_010.png"
    };
           
至此我們可以先寫個簡單搜尋工具
JavaFx入門2 - JavaFX Scene Builder 2.0 建構fxml
對應fxml代碼
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.image.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox fx:id="chooseV" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sugar.base2.controller.SearchController">

    <children>
      <ToolBar fx:id="topBar" prefHeight="40.0" prefWidth="200.0">
         <items>

            <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Text" />
            <TextField fx:id="searchTv" />
            <DatePicker />
         </items>
      </ToolBar>
      <ScrollPane fx:id="scPane" prefHeight="362.0" prefWidth="600.0" VBox.vgrow="ALWAYS">
         <content>
            <GridPane fx:id="gridPane" prefHeight="347.0" prefWidth="596.0" VBox.vgrow="ALWAYS">
              <columnConstraints>
                <ColumnConstraints hgrow="NEVER" minWidth="200.0" prefWidth="200.0" />
                 <ColumnConstraints hgrow="NEVER" minWidth="200.0" prefWidth="200.0" />
                  <ColumnConstraints hgrow="NEVER" minWidth="200.0" prefWidth="200.0" />
              </columnConstraints>
              <rowConstraints>
                   <!--  動态添加行數 建議放置一個RowConstraints模闆在這裡!!!!!-->
                  <RowConstraints minHeight="200.0" prefHeight="200.0" vgrow="NEVER" />
              </rowConstraints>
            </GridPane>
         </content>
      </ScrollPane>
   </children>
</VBox>

           
對應Controller
package sugar.base2.controller;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.control.ToolBar;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
import sugar.base2.onlyOp.OnlyReadRes;
import sugar.utils.fxapp.GridPaneUtils;
import sugar.utils.fxapp.ResourcesUtils;


import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

public class SearchController implements Initializable {
   //Java注解 gridPane對應<GridPane fx:id="gridPane" prefHeight="347.0" prefWidth="596.0" VBox.vgrow="ALWAYS"> 裡的fx:id="gridPane"
    @FXML
    GridPane gridPane;

    @FXML
    TextField searchTv;

    @FXML
    ToolBar topBar;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        //back
        ImageView back = new ImageView(new Image(ResourcesUtils.
                getJarFilePath(OnlyReadRes.backPic)));
        back.setFitHeight(32);
        back.setFitWidth(32);
        topBar.getItems().add(0,back);


        //輸入框發生變化觸發
        searchTv.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
               // System.out.println("輸入框發生變化!!");
              requestNetData(newValue);// 請求網絡json資料

            }
        });


    }
    private void requestNetData(String v){
        //先清空
        gridPane.getChildren().clear();

        GridPane pane = new GridPane();
        pane.getChildren().add(0,new Text(v));

        List<GridPane> lists = new ArrayList<>();
        //添加到GridPane
        GridPaneUtils.setNodeToPane(gridPane,lists);
    }


}

           
其中這段代碼是添加一個ImageView到ToolBar 上面,并放在第0個位置
ImageView back = new ImageView(new Image(ResourcesUtils.
                getJarFilePath(OnlyReadRes.backPic)));
        back.setFitHeight(32);
        back.setFitWidth(32);
        topBar.getItems().add(0,back); //添加一個ImageView到ToolBar 上面,并放在第0個位置
           
當輸入框輸入值發生變化時,可以自己寫個Spring Boot背景服務擷取資訊。
//輸入框發生變化觸發
        searchTv.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
               // System.out.println("輸入框發生變化!!");
              requestNetData(newValue);// 請求網絡json資料等

            }
        });
           
//
    private void requestNetData(String v){
        //先清空
        gridPane.getChildren().clear();

        GridPane pane = new GridPane();
        pane.getChildren().add(0,new Text(v));

        List<GridPane> lists = new ArrayList<>();
        //添加到GridPane
        GridPaneUtils.setNodeToPane(gridPane,lists);
    }
           
小結
//前面說過
//這是基于java注解  searchTv對應fxml裡面聲明的 fx:id="searchTv"
// <TextField fx:id="searchTv" />
 @FXML
    TextField searchTv;
           
常見控件 以及随視窗(父容器)自動拉伸(Java代碼)

ImageView

Image image = new Image(getClass().getResourceAsStream("/test.jpg"));
		ImageView imageView = new ImageView();
		imageView.setImage(image);
           

ToolBar

ToolBar toolBar = new ToolBar();
toolBar.setOrientation(Orientation.HORIZONTAL);//設定布局是水準
           

TabPane

TabPane tabPane = new TabPane();
		// 放置位置
		tabPane.setSide(Side.TOP);		tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.SELECTED_TAB);	
           

MenuBar

MenuBar menuBar = new MenuBar();
			Menu menuFile = new Menu("檔案");
			Menu menuEdit = new Menu("編輯");
			Menu menuWindow = new Menu("視窗");
			Menu menuSetting = new Menu("設定");
			// 添加到MenuBar
			menuBar.getMenus().add(menuFile);
			menuBar.getMenus().add(menuEdit);
			menuBar.getMenus().add(menuWindow);
			menuBar.getMenus().add(menuSetting);

			MenuItem menuItem = new MenuItem("建立");
			menuFile.getItems().add(menuItem);	
           

SplitPane

SplitPane splitPane = new SplitPane();
splitPane.setOrientation(Orientation.VERTICAL);//設定是垂直
splitPane.getItems().addAll(p1, p2);//添加控件
splitPane.setResizableWithParent(p2, true);//p2控件不随SplitPane變大而變大
splitPane.setResizableWithParent(p1, false); //p1控件随SplitPane變大而變大
splitPane.setDividerPositions(0.2f, 0.8f);	//分隔條位置	
           

ScrollPane

ScrollPane scrollPane = new ScrollPane();
		scrollPane.setContent(new TextArea());
		scrollPane.setFitToHeight(true);//ScrollPane裡面控件可以随ScrollPane高度變高自适應(變高)
		scrollPane.setFitToWidth(true);//ScrollPane裡面控件可以随ScrollPane高度變寬自适應(變寬)
           
在fxml裡的寫法

ScrollPane

//參考上面代碼
 <ScrollPane fitToHeight="true" fitToWidth="true" prefViewportHeight="271.0" prefViewportWidth="200.0" >
           

GridPane

//VBox.vgrow="ALWAYS" 總數随着Vbox變化而變化
GridPane fx:id="gridPane" prefHeight="347.0" prefWidth="596.0" VBox.vgrow="ALWAYS">
           
更多控件方法待更新