spring javafx_带有Spring的JavaFX 2

本文介绍了如何将Spring框架与JavaFX 2结合使用,以改进桌面应用的开发体验。文章讨论了JavaFX的发展历程,从Swing到JavaFX的转变,以及JavaFX 2引入的XML标记语言FXML,使得视图描述更加直观。此外,文章提到了Eclipse的FXclipse插件和Scene Builder,以及Scenic View工具,这些工具对JavaFX应用的开发和调试提供了支持。作者通过代码示例展示了如何使用SpringFxmlLoader和自定义ControllerFactory实现Spring管理的控制器,并给出了一个简单的搜索功能的实现。文章结论是,JavaFX 2与Spring的结合为桌面应用开发提供了强大的功能和便利性。
摘要由CSDN通过智能技术生成

spring javafx

我将从一个大胆的声明开始:我一直很喜欢Java Swing或applet。 在那里,我说了。 如果我进行一些自我分析,那么这种钦佩可能是在我接触Java时开始的。 Swing(实际上)是我使用Java所做的第一件事,它给出了一些统计结果,并使我能够使用该语言做一些事情。 在我年轻的时候,我们建立了一些自制的胖客户来管理我们的3.5英寸软盘/ CD集合(用VB编写,然后用Basic编写),这可能也起到了作用。

无论如何,足以说明我的个人稀缺性。 事实是,Swing帮助许多人构建了出色的应用程序,但众所周知,Swing有其缺点。 对于初学者来说,很长一段时间以来一直没有发展。 如果还需要很多样板代码

您想要创建高质量的代码。 它带有一些古怪的设计“缺陷”,缺少MVC等现成的模式。 样式有点局限性,因为您必须依靠有限的L&F架构,默认情况下不会内置I18N。 可以说现在开发Swing很好,基本上可以追溯到过去。

幸运的是,Oracle几年前通过启动JavaFX试图改变这一状况。 我记得在Devoxx(或Javapolis,当时的名称)上被引入JavaFX。 漂亮的演示程序看起来非常有前途,所以我很高兴看到Swing的继任者终于来了。 从我看到其内部结构的那一刻起,情况就发生了变化。 它的主要缺点之一是它基于一种黑暗的新语法(称为JavaFX脚本)。 如果您从未见过JavaFX脚本; 它看起来像Java,JSON和JavaScript之间的怪异品种。 尽管已将其编译为Java字节码,并且可以使用其中的Java API,但与Java的集成从未真正好。

语言本身(尽管功能很强大)要求您花费大量时间来了解细节,以便最终获得源代码,但是这次比普通的Java代码更难管理和支持。 事实证明,我并不是唯一的一个。 很多人都感到相同(当然也有其他原因),JavaFX从来没有取得过成功。 但是,不久前Oracle通过引入JavaFX 2改变了潮流。

首先,他们摆脱了JavaFX脚本(不再受支持)并将其转变为真正的本机Java SE API(JavaFX 2.2.3是Java 7 SE更新6的一部分)。 JavaFX API现在看起来更像是熟悉的Swing API,这是一件好事。 它为您提供了布局管理器的外观,事件侦听器以及您以前习惯的所有其他组件,但效果甚至更好。 因此,如果您希望像Swing一样编写JavaFX,尽管语法和改进的体系结构稍有不同。 现在也可以
将现有的Java Swing应用程序与JavaFX混合在一起

但是还有更多。 他们引入了一种基于XML的标记语言,使您可以描述视图。 这具有一些优点,首先是XML编码比Java编码更快。 与Java相比,可以更容易地生成XML,并且用于描述视图的语法也更加紧凑。 使用某种标记表示视图也更加直观,尤其是如果您曾经做过一些Web开发。 因此,可以拥有以FXML描述的视图(即其调用方式),应用程序控制器与该视图分离(在Java中是这样)和在CSS中的样式(是的,因此不再有L&F,CSS支持是标准的)。 您仍然可以直接在FXML中嵌入Java(或其他语言)。 但这可能不是您想要的(脚本反模式)。 另一个好处是对绑定的支持。 通过将fx:id属性放在视图组件上,并将@FXML批注放在应用程序控制器中的实例变量上,可以将视图中的每个组件绑定到应用程序控制器。 然后将自动注入相应的元素,因此您可以在应用程序控制器内部更改其数据或行为。 事实证明,使用一些代码行,您就可以轻松集成所选的DI框架,这不是很好吗? 那工具呢?

好吧,首先,有一个用于Eclipse的插件(fxclipse),它将动态呈现FXML。 您可以通过Eclipse市场安装它:

该插件将立即呈现您进行的任何调整:

请注意,至少需要JDK7u6才能使该插件正常工作。 如果您的JDK太旧,则会在Eclipse中看到一个空白窗格。 另外,如果您创建JavaFX项目,则需要将jfxrt.jar手动放入构建类路径中。 您可以在%JAVA_HOME%/ jre / lib中找到此文件。

直到知道该插件在视觉上(通过拖放)对您没有帮助,但那里有一个单独的IDE:
现场建设者 。 该构建器也集成在Netbeans中,对于AFAIK,尚不支持eclipse,因此如果要使用它,则必须单独运行它。 该构建器允许您使用拖放方式以可视方式开发FXML。 细节不错; 场景构建器实际上是用JavaFX编写的。 然后,您还有一个名为Scenic View的单独的应用程序,它对正在运行的JavaFX应用程序进行自省,并显示其构建方式。 您将获得具有不同节点及其层次结构的图。 对于每个节点,您可以看到其属性等等:

好的,让我们从一些代码示例开始。 我要做的第一件事是在场景生成器中设计演示应用程序:

我通过将容器/控制器d&d放在视图上以图形方式进行了此操作。 我还提供了要绑定到我的视图和fx:id的控件,您也可以通过场景生成器来做到这一点:

特别是对于按钮,我还添加了onAction(单击按钮后应在控制器上执行的方法):

接下来,我在Eclipse的源代码视图中手动添加了控制器。 每个FXML只能有一个控制器,应该在顶层元素中声明它。 我制作了两个FXML,一个代表主屏幕,另一个代表菜单栏。 您可能希望将逻辑划分为多个控制器,而不是在单个控制器中塞满很多东西–在这里,单一职责是一个很好的设计指南。 第一个FXML是“ search.fxml”,代表搜索条件和结果视图:

<?xml version="1.0" encoding="UTF-8"?>
 
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
 
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="be.error.javafx.controller.SearchController">
  <children>
    <SplitPane dividerPositions="0.39195979899497485" focusTraversable="true" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
      <items>
        <GridPane fx:id="grid" prefHeight="91.0" prefWidth="598.0">
          <children>
     <fx:include source="/menu.fxml"/>
            <GridPane prefHeight="47.0" prefWidth="486.0" GridPane.columnIndex="1" GridPane.rowIndex="5">
              <children>
                <Button fx:id="clear" cancelButton="true" mnemonicParsing="false" onAction="#clear" text="Clear" GridPane.columnIndex="1" GridPane.rowIndex="1" />
                <Button fx:id="search" defaultButton="true" mnemonicParsing="false" onAction="#search" text="Search" GridPane.columnIndex="2" GridPane.rowIndex="1" />
              </children>
              <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="338.0" minWidth="10.0" prefWidth="338.0" />
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="175.0" minWidth="0.0" prefWidth="67.0" />
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="175.0" minWidth="10.0" prefWidth="81.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints maxHeight="110.0" minHeight="10.0" prefHeight="10.0" vgrow="SOMETIMES" />
                <RowConstraints maxHeight="72.0" minHeight="10.0" prefHeight="40.0" vgrow="SOMETIMES" />
              </rowConstraints>
            </GridPane>
            <Label alignment="CENTER_RIGHT" prefHeight="21.0" prefWidth="101.0" text="Product name:" GridPane.columnIndex="0" GridPane.rowIndex="1" />
            <TextField fx:id="productName" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <Label alignment="CENTER_RIGHT" prefWidth="101.0" text="Min price:" GridPane.columnIndex="0" GridPane.rowIndex="2" />
            <Label alignment="CENTER_RIGHT" prefWidth="101.0" text="Max price:" GridPane.columnIndex="0" GridPane.rowIndex="3" />
            <TextField fx:id="minPrice" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
            <TextField fx:id="maxPrice" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
          </children>
          <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="246.0" minWidth="10.0" prefWidth="116.0" />
            <ColumnConstraints fillWidth="false" hgrow="SOMETIMES" maxWidth="537.0" minWidth="10.0" prefWidth="482.0" />
          </columnConstraints>
          <rowConstraints>
            <RowConstraints maxHeight="64.0" minHeight="10.0" prefHeight="44.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="68.0" minHeight="0.0" prefHeight="22.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="68.0" minHeight="10.0" prefHeight="22.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="68.0" minHeight="10.0" prefHeight="22.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="167.0" minHeight="10.0" prefHeight="14.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="167.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES" />
          </rowConstraints>
        </GridPane>
        <StackPane prefHeight="196.0" prefWidth="598.0">
          <children>
            <TableView fx:id="table" prefHeight="200.0" prefWidth="200.0">
              <columns>
                <TableColumn prefWidth="120.0" resizable="true" text="OrderId">
                  <cellValueFactory>
                    <PropertyValueFactory property="orderId" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="CustomerId">
                  <cellValueFactory>
                    <PropertyValueFactory property="customerId" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="#products">
                  <cellValueFactory>
                    <PropertyValueFactory property="productsCount" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="Delivered">
                  <cellValueFactory>
                    <PropertyValueFactory property="delivered" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="Delivery days">
                  <cellValueFactory>
                    <PropertyValueFactory property="deliveryDays" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="150.0" text="Total order price">
                  <cellValueFactory>
                    <PropertyValueFactory property="totalOrderPrice" />
                  </cellValueFactory>
                </TableColumn>
              </columns>
            </TableView>
          </children>
        </StackPane>
      </items>
    </SplitPane>
  </children>
</StackPane>

在第11行上,您可以看到我配置了应与视图一起使用的应用程序控制器类。 在第17行,您可以看到单独的menu.fxml的导入,如下所示:

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

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

<Pane prefHeight='465.0' prefWidth='660.0' xmlns:fx='http://javafx.com/fxml' fx:controller='be.error.javafx.controller.FileMenuController'>
 <children>
  <MenuBar layoutX='0.0' layoutY='0.0'>
   <menus>
    <Menu mnemonicParsing='false' text='File'>
     <items>
      <MenuItem text='Exit' onAction='#exit' /> 
     </items>
    </Menu>
   </menus>
  </MenuBar>
 </children>
</Pane>

在第7行,您可以看到它使用了另一个控制器。 在Eclipse中,如果从插件打开fxclipse视图,则将获得与场景构建器相同的渲染视图。 如果您想在代码中进行少量更改以使其直接反映出来,那么它很方便:启动应用程序的代码非常标准:

package be.error.javafx;

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApplication extends Application {

 private static final SpringFxmlLoader loader = new SpringFxmlLoader();

 @Override
 public void start(Stage primaryStage) {
  Parent root = (Parent) loader.load('/search.fxml');
  Scene scene = new Scene(root, 768, 480);
  primaryStage.setScene(scene);
  primaryStage.setTitle('JavaFX demo');
  primaryStage.show();
 }

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

唯一需要注意的是我们从Application扩展。 这是一个样板代码,例如,它将确保UI的创建发生在JavaFX应用程序线程上。 您可能还记得Swing中的此类故事,其中每个UI交互都需要在事件分派器线程(EDT)上发生,JavaFX也是一样。 当您被应用程序回调时,默认情况下您处于“右线程”状态(例如,动作侦听器等方法)。 但是,如果您启动应用程序或在单独的线程中执行长时间运行的任务,则需要确保在正确的线程上启动UI交互。 挥杆你会用
JavaFX的SwingUtilities.invokeLater()Platform.runLater()

更特别的是我们的SpringFxmlLoader:

package be.error.javafx;

import java.io.IOException;
import java.io.InputStream;

import javafx.fxml.FXMLLoader;
import javafx.util.Callback;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringFxmlLoader {

 private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

 public Object load(String url) {
  try (InputStream fxmlStream = SpringFxmlLoader.class
    .getResourceAsStream(url)) {
   System.err.println(SpringFxmlLoader.class
    .getResourceAsStream(url));
   FXMLLoader loader = new FXMLLoader();
   loader.setControllerFactory(new Callback<Class<?>, Object>() {
    @Override
    public Object call(Class<?> clazz) {
     return applicationContext.getBean(clazz);
    }
   });
   return loader.load(fxmlStream);
  } catch (IOException ioException) {
   throw new RuntimeException(ioException);
  }
 }
}

高亮显示的行显示了自定义ControllerFactory。 无需设置此JavaFX即可简单地实例化您在FXML中指定为控制器的类,而无需任何特殊操作。 在这种情况下,该类将不受Spring管理(除非您将使用CTW / LTW AOP)。 通过指定自定义工厂,我们可以定义控制器的实例化方式。 在这种情况下,我们从应用程序上下文中查找bean。 最后,我们有两个控制器SearchController:

package be.error.javafx.controller;

import java.math.BigDecimal;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import be.error.javafx.model.Order;
import be.error.javafx.model.OrderSearchCriteria;
import be.error.javafx.model.OrderService;

public class SearchController implements Initializable {

 @Autowired
 private OrderService orderService;
 @FXML
 private Button search;
 @FXML
 private TableView<Order> table;
 @FXML
 private TextField productName;
 @FXML
 private TextField minPrice;
 @FXML
 private TextField maxPrice;

 @Override
 public void initialize(URL location, ResourceBundle resources) {
  table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
 }

 public void search() {
  OrderSearchCriteria orderSearchCriteria = new OrderSearchCriteria();
  orderSearchCriteria.setProductName(productName.getText());
  orderSearchCriteria
    .setMaxPrice(StringUtils.isEmpty(minPrice.getText()) ? null:new BigDecimal(minPrice.getText()));
  orderSearchCriteria
    .setMinPrice(StringUtils.isEmpty(minPrice.getText()) ? null: new BigDecimal(minPrice.getText()));
  ObservableList<Order> rows = FXCollections.observableArrayList();
  rows.addAll(orderService.findOrders(orderSearchCriteria));
  table.setItems(rows);
 }

 public void clear() {
  table.setItems(null);
  productName.setText('');
  minPrice.setText('');
  maxPrice.setText('');
 }
}

高亮显示的行按各自的顺序排列:

  • 由Spring自动注入,这是我们的Spring托管服务,将用于从中查找数据
  • JavaFX自动注入,我们需要在控制器中进行操作或读取的控件
  • 一种特殊的init方法来初始化我们的表,以便在视图放大时列将自动调整大小
  • 按下搜索按钮时将调用的操作侦听器样式回调
  • 按下清除按钮时将调用的操作侦听器样式回调

最后,FileMenuController除了关闭我们的应用程序外没有其他特殊功能:

package be.error.javafx.controller;

import javafx.application.Platform;
import javafx.event.ActionEvent;

public class FileMenuController {

 public void exit(ActionEvent actionEvent) {
  Platform.exit();
 }
}

最后(不太令人兴奋)结果:


搜索后:

使视图更宽,也拉长了列:

文件菜单允许我们退出:

在玩完JavaFX2之后,我印象深刻。 还有越来越多的控件(我相信已经有了浏览器控件之类)。 所以我认为我们在这里是正确的。

参考:来自Koen Serneels –技术博客博客的JCG合作伙伴 Koen Serneels提供的带有Spring的JavaFX 2

翻译自: https://www.javacodegeeks.com/2013/03/javafx-2-with-spring.html

spring javafx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值