天天看点

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">
           
更多控件方法待更新