我需要帮助创建null
- 安全BooleanBinding
。它必须是null
- 安全,因为我不能为模型中的所有属性提供默认值(一个原因:模型包含枚举)。我的第一种方法如下:
executeButtonDisabled.bind(missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.CREATED)));
final BooleanBinding isNotExecutingBinding = missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.EXECUTING));
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding)
但是这种方法不起作用,因为评估完整的表达式导致NullPointerException
(但是当提供属性时它正确地更新了按钮)。现在我正在尝试使用JavaFX binding and null values中建议的Bindings
类,但我无法使其正常工作。这是我目前的做法:
final BooleanBinding isNotCreatedBinding = Bindings.createBooleanBinding(
() -> mission.isNull().getValue()
? true
: missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.CREATED).getValue());
final BooleanBinding isNotExecutingBinding = Bindings.createBooleanBinding(
() -> mission.isNull().getValue()
? true
: missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.EXECUTING).getValue());
executeButtonDisabled.bind(isNotCreatedBinding);
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding);
但这不起作用,我不明白为什么。似乎modelProperty()
的属性绑定在这里不起作用!您能否向我解释如何将第一个工作解决方案(至少没有null
)转换为正确的null
安全解决方案?
编辑2016-04-26:建议的解决方案不起作用,因此我创建了一个简单的完整工作示例:
Mission.java
:
package de.florianwolters.example.javafx.bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Mission {
enum Status {
CREATED,
EXECUTING,
COMPLETED,
CANCELED;
}
private final StringProperty shortName = new SimpleStringProperty();
private final ObjectProperty<Status> status = new SimpleObjectProperty<>();
public Mission(final String shortName) {
this.setShortName(shortName);
this.setStatus(Status.CREATED);
}
public String getShortName() {
return shortNameProperty().get();
}
public void setShortName(final String shortName) {
shortNameProperty().set(shortName);
}
public StringProperty shortNameProperty() {
return shortName;
}
public Status getStatus() {
return statusProperty().get();
}
public void setStatus(final Status status) {
statusProperty().set(status);
}
public ObjectProperty<Status> statusProperty() {
return status;
}
}
MissionDetailsViewModel.java
:
package de.florianwolters.example.javafx.bindings;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleObjectProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class MissionDetailsViewModel {
/**
* The logger used for logging in the `MissionDetailsViewModel` class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(
MissionDetailsViewModel.class);
private ObjectProperty<Mission> mission = new SimpleObjectProperty<>();
private final ReadOnlyBooleanWrapper executeButtonDisabled = new ReadOnlyBooleanWrapper(true);
private final ReadOnlyBooleanWrapper completeButtonDisabled = new ReadOnlyBooleanWrapper(true);
private final ReadOnlyBooleanWrapper cancelButtonDisabled = new ReadOnlyBooleanWrapper(true);
/**
* Constructs a `MissionDetailsViewModel`.
*/
public MissionDetailsViewModel(final ObjectProperty<Mission> mission) {
this.mission.bind(mission);
// partialWorkingBinding();
notWorkingBinding();
}
private void notWorkingBinding() {
final BooleanBinding isNotCreatedBinding = Bindings.createBooleanBinding(
() -> missionProperty().isNull().get()
? true
: missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.CREATED).get(),
missionProperty());
final BooleanBinding isNotExecutingBinding = Bindings.createBooleanBinding(
() -> mission.isNull().get()
? true
: missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.EXECUTING).get(),
missionProperty());
executeButtonDisabled.bind(isNotCreatedBinding);
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding);
}
private void partialWorkingBinding() {
executeButtonDisabled.bind(missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.CREATED)));
final BooleanBinding isNotExecutingBinding = missionProperty().isNotNull().and(missionProperty().get().statusProperty().isNotEqualTo(Mission.Status.EXECUTING));
completeButtonDisabled.bind(isNotExecutingBinding);
cancelButtonDisabled.bind(isNotExecutingBinding);
}
public boolean isExecuteButtonDisabled() {
return executeButtonDisabledProperty().get();
}
public ReadOnlyBooleanProperty executeButtonDisabledProperty() {
return executeButtonDisabled;
}
public boolean isCompleteButtonDisabled() {
return completeButtonDisabledProperty().get();
}
public ReadOnlyBooleanProperty completeButtonDisabledProperty() {
return completeButtonDisabled;
}
public boolean isCancelButtonDisabled() {
return cancelButtonDisabledProperty().get();
}
public ReadOnlyBooleanProperty cancelButtonDisabledProperty() {
return cancelButtonDisabled;
}
public Mission getMission() {
return missionProperty().get();
}
public void setMission(final Mission mission) {
missionProperty().set(mission);
}
public ObjectProperty<Mission> missionProperty() {
return mission;
}
}
MissionDetailsViewModelTest.java
:
package de.florianwolters.example.javafx.bindings;
import static eu.lestard.assertj.javafx.api.Assertions.assertThat;
import javafx.beans.property.SimpleObjectProperty;
import org.junit.Before;
import org.junit.Test;
public final class MissionDetailsViewModelTest {
private Mission mission;
private MissionDetailsViewModel viewModel;
@Before
public void setUp() {
mission = new Mission("My Short Name");
viewModel = new MissionDetailsViewModel(new SimpleObjectProperty<Mission>(mission));
}
@Test
public void testInitialValues() {
assertThat(viewModel.executeButtonDisabledProperty()).isFalse();
assertThat(viewModel.completeButtonDisabledProperty()).isTrue();
assertThat(viewModel.cancelButtonDisabledProperty()).isTrue();
}
@Test
public void testMissionStatusSetToExecuting() {
mission.setStatus(Mission.Status.EXECUTING);
assertThat(viewModel.executeButtonDisabledProperty()).isTrue();
assertThat(viewModel.completeButtonDisabledProperty()).isFalse();
assertThat(viewModel.cancelButtonDisabledProperty()).isFalse();
}
@Test
public void testMissionStatusSetToCompleted() {
mission.setStatus(Mission.Status.COMPLETED);
assertThat(viewModel.executeButtonDisabledProperty()).isTrue();
assertThat(viewModel.completeButtonDisabledProperty()).isTrue();
assertThat(viewModel.cancelButtonDisabledProperty()).isTrue();
}
@Test
public void testMissionStatusSetToCanceled() {
mission.setStatus(Mission.Status.CANCELED);
assertThat(viewModel.executeButtonDisabledProperty()).isTrue();
assertThat(viewModel.completeButtonDisabledProperty()).isTrue();
assertThat(viewModel.cancelButtonDisabledProperty()).isTrue();
}
}
单元测试失败并显示上面的代码(使用方法notWorkingBinding()
),但使用方法partialWorkingBinding()
。我做错了什么?
答案 0 :(得分:3)
您为isNotCreatedBinding
设置了计算功能,但您没有设置绑定的依赖关系。您需要添加mision
作为依赖项:
Bindings.createBooleanBinding(
() -> mission.isNull().getValue()
? true
: missionProperty().get().statusProperty().isNotEqualTo(MissionStatus.CREATED).getValue(), mission);
修改强>
您需要侦听statusProperty而不是missionProperty,当missionProperty()。get()== null时,它不会与createBooleanBinding
一起使用。
但您可以使用 When
绑定:
(正如问题中已经提到的NullPointerException
)
BooleanBinding isNotCreatedBinding = new When(mission.isNotNull()).then(mission.get().statusProperty().isNotEqualTo(Mission.Status.CREATED)).otherwise(false);
或更低级别的解决方案:
missionProperty().addListener((ov, m, m1) -> {
if (m1 != null) {
executeButtonDisabled.bind(m1.statusProperty().isNotEqualTo(Mission.Status.CREATED));
}else {
executeButtonDisabled.unbind();
executeButtonDisabled.set(false);
}
});
答案 1 :(得分:2)
Tomas Mikula的ReactFX framework (v 2.0)内置了此功能:
import org.reactfx.value.Val;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
public class NestedBindingTest {
public static void main(String[] args) {
BooleanProperty disable = new SimpleBooleanProperty();
disable.addListener((obs, wasDisabled, isNowDisabled) ->
System.out.println("disable: "+wasDisabled+" -> "+isNowDisabled));
ObjectProperty<Item> item = new SimpleObjectProperty<>();
Val<Item.Status> status = Val.flatMap(item, Item::statusProperty);
disable.bind(status.map(s -> s == Item.Status.PENDING).orElseConst(true));
Item i = new Item();
System.out.println("Setting item");
item.set(i);
System.out.println("Setting item status to PENDING");
i.setStatus(Item.Status.PENDING);
System.out.println("Setting item status to READY");
i.setStatus(Item.Status.READY);
System.out.println("Setting item to null");
item.set(null);
}
public static class Item {
public enum Status {PENDING, READY}
private final ObjectProperty<Status> status = new SimpleObjectProperty<>();
public final ObjectProperty<Status> statusProperty() {
return this.status;
}
public final NestedBindingTest.Item.Status getStatus() {
return this.statusProperty().get();
}
public final void setStatus(final NestedBindingTest.Item.Status status) {
this.statusProperty().set(status);
}
}
}