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