JavaFX绑定同步两个值:当依赖变量更改时,其他变量更改。
要将属性绑定到另一个属性,请调用bind()方法,该方法在一个方向绑定值。 例如,当属性A绑定到属性B时,属性B的更改将更新属性A,但不可以反过来。
绑定选项
JavaFX提供了许多绑定选项,以便在域对象和GUI控件中的属性之间进行同步。
我们可以在JavaFX的Properties API中使用以下三种绑定策略:
- Java Bean上的双向绑定
- 与Fluent API的高级绑定
- 使用
javafx.beans.binding
定义绑定对象进行低级绑定。
双向绑定
双向绑定绑定相同类型的属性,并同步两侧的值。当使用bindBidirectional()
方法双向绑定时,需要两个属性都必须是可读/写的。
以下代码显示如何在firstName
属性和字符串属性变量之间进行双向绑定
import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty
public class Main {
public static void main(String[] args) {
User contact = new User("Jame", "Bind")
StringProperty fname = new SimpleStringProperty()
fname.bindBidirectional(contact.firstNameProperty())
contact.firstNameProperty().set("new value")
fname.set("新绑定名称值")
System.out.println("firstNameProperty = " + contact.firstNameProperty().get())
System.out.println("fname = " + fname.get())
}// @ W WW .yI iB AI.c o M
}
class User {
private SimpleStringProperty firstName = new SimpleStringProperty()
private SimpleStringProperty lastName = new SimpleStringProperty()
public User(String fn, String ln) {
firstName.setValue(fn)
lastName.setValue(ln)
}
public final String getFirstName() {
return firstName.getValue()
}
public StringProperty firstNameProperty() {
return firstName
}
public final void setFirstName(String firstName) {
this.firstName.setValue(firstName)
}
public final String getLastName() {
return lastName.getValue()
}
public StringProperty lastNameProperty() {
return lastName
}
public final void setLastName(String lastName) {
this.lastName.setValue(lastName)
}
}
上面的代码生成以下结果。
firstNameProperty = 新绑定名称值
fname = 新绑定名称值
高级别绑定
我们也可以使用JavaFX流利的API来绑定属性。API使用类似英语的方法名称对属性执行操作。 例如,multiply()
,divide()
,subtract()
,isEqualTo()
,isNotEqualTo()
,concat()
。 请注意,方法名称中没有get
或set
。当链接API在一起时可以写代码,就像类似于写英文句子,例如,width().multiply(height()).divide(2)
。
以下代码显示如何创建表示计算矩形面积的公式的属性。
它通过使用javafx.beans.binding.IntegerExpression
接口中的fluent API来执行高级绑定。
该代码使用multiply()
方法,该方法返回包含计算值 - NumberBinding
。
这个绑定是延迟评估求值的,这意味着乘法不会执行计算,除非通过get()
或getValue()
方法调用属性的值。
import javafx.beans.binding.NumberBinding
import javafx.beans.property.IntegerProperty
import javafx.beans.property.SimpleIntegerProperty
public class Main {
public static void main(String[] args) {
// Area = width * height
IntegerProperty width = new SimpleIntegerProperty(10)
IntegerProperty height = new SimpleIntegerProperty(10)
NumberBinding area = width.multiply(height)
System.out.println(area.getValue())
}
}
上面的代码生成以下结果。
100
低级别绑定
当对NumberBinding
类进行子类化时,使用低级别绑定,例如Double
类型的DoubleBinding
类。
在DoubleBinding
类的子类中,我们覆盖它的computeValue()
方法,以便可以使用运算符(例如*
和 -
)来制定复杂的数学方程计算。
高级绑定使用如multiply()
,subtract()
等方法,而低级绑定使用诸如*
和 -
等运算符。
以下代码显示如何为公式创建低级别绑定,来计算矩形的面积。
import javafx.beans.binding.DoubleBinding
import javafx.beans.property.DoubleProperty
import javafx.beans.property.SimpleDoubleProperty
public class Main {
public static void main(String[] args) {
DoubleProperty width = new SimpleDoubleProperty(2)
DoubleProperty height = new SimpleDoubleProperty(2)
DoubleBinding area = new DoubleBinding() {
{
super.bind(width, height) // initial bind
}
@Override
protected double computeValue() {
return width.get() * height.get()
}
}
System.out.println(area.get())
}
}
上面的代码生成以下结果。
4.0
UI控件和域模型之间的绑定
在JavaFX中,UI控件和域模型之间的数据绑定很容易。以下代码显示如何创建登录对话框并绑定用户域对象。
首先,我们定义域对象,它是描述用户名和密码的JavaFX JavaBean。
class User {
private final ReadOnlyStringWrapper userName
private StringProperty password
public User() {
userName = new ReadOnlyStringWrapper(this, "userName", "ABC")
password = new SimpleStringProperty(this, "password", "")
}
public final String getUserName() {
return userName.get()
}
public ReadOnlyStringProperty userNameProperty() {
return userName.getReadOnlyProperty()
}
public final String getPassword() {
return password.get()
}
public StringProperty passwordProperty() {
return password
}
}
我们创建了两个UI控件,一个用于使用Text
控件显示用户名,另一个是 PasswordField
字段控件,它将输入值绑定到域对象(User
)中的 password
字段。
Text userName = new Text()
userName.textProperty().bind(user.userNameProperty())
PasswordField passwordField = new PasswordField()
passwordField.setPromptText("Password")
user.passwordProperty().bind(passwordField.textProperty())
在更改侦听器中为passwordField
字段文本值属性设置BooleanProperty
访问权限。
passwordField.textProperty().addListener((obs, ov, nv) -> {
boolean granted = passwordField.getText().equals(MY_PASS)
accessGranted.set(granted)
if (granted) {
primaryStage.setTitle("")
}
})
在enter键点击事件中访问BooleanProperty accessGranted
。
// user hits the enter key
passwordField.setOnAction(actionEvent -> {
if (accessGranted.get()) {
System.out.println("granted access:"+ user.getUserName())
System.out.println("password:"+ user.getPassword())
Platform.exit()
} else {
primaryStage.setTitle("no access")
}
})
完整的源代码如下所示。
import javafx.application.Application
import javafx.application.Platform
import javafx.beans.property.BooleanProperty
import javafx.beans.property.ReadOnlyStringProperty
import javafx.beans.property.ReadOnlyStringWrapper
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleStringProperty
import javafx.beans.property.StringProperty
import javafx.scene.Group
import javafx.scene.Scene
import javafx.scene.control.PasswordField
import javafx.scene.layout.VBox
import javafx.scene.text.Text
import javafx.stage.Stage
// @ w Ww .yi iB A i. C o M
public class Main extends Application {
private final static String MY_PASS = "passwd"// 初始密码
private final static BooleanProperty accessGranted = new SimpleBooleanProperty(false)
@Override
public void start(Stage primaryStage) {
User user = new User()
Group root = new Group()
Scene scene = new Scene(root, 320, 100)
primaryStage.setScene(scene)
Text userName = new Text()
userName.textProperty().bind(user.userNameProperty())
PasswordField passwordField = new PasswordField()
passwordField.setPromptText("Password")
user.passwordProperty().bind(passwordField.textProperty())
// user hits the enter key
passwordField.setOnAction(actionEvent -> {
if (accessGranted.get()) {
System.out.println("granted access:" + user.getUserName())
System.out.println("password:" + user.getPassword())
Platform.exit()
} else {// @ WW w .yIIB A i.c OM
primaryStage.setTitle("no access")
}
})
passwordField.textProperty().addListener((obs, ov, nv) -> {
boolean granted = passwordField.getText().equals(MY_PASS)
accessGranted.set(granted)
if (granted) {
primaryStage.setTitle("")
}
})
VBox formLayout = new VBox(4)
formLayout.getChildren().addAll(userName, passwordField)
formLayout.setLayoutX(12)
formLayout.setLayoutY(12)
root.getChildren().addAll(formLayout)
primaryStage.show()
}
public static void main(String[] args) {
launch(args)
}
}
class User {
private final ReadOnlyStringWrapper userName
private StringProperty password
public User() {
userName = new ReadOnlyStringWrapper(this, "userName", "ABC")
password = new SimpleStringProperty(this, "password", "")
}
public final String getUserName() {
return userName.get()
}
public ReadOnlyStringProperty userNameProperty() {
return userName.getReadOnlyProperty()
}
public final String getPassword() {
return password.get()
}
public StringProperty passwordProperty() {
return password
}
}
上面的代码生成以下结果。
在上面的输入框中输入密码:passwd
完成后,得到以下输出结果 -
granted access:ABC
password:passwd