我的情况是我有PropertyList<ParentObject>
而ParentObject
有一个ObjectProperty<ChildObject>
,其中包含一组属性。我想强制ChildObject
在某些属性发生变化时被标记为无效。这样我就可以提取它们了。
我尝试使用SimpleListProperty
提取器参数和绑定直接提取它们,但它没有用。
我要求做一些我不应该做的事吗?干净的方法是什么?
这是一个有点人为的例子。目标是让管理员计数正确显示用户具有管理员角色的ApplicationInstances数量。
public class MCVE extends Application {
ObjectProperty<Model> model = new SimpleObjectProperty<>(this, "model", new Model());
private static ObservableList<UserRole> userRoles = FXCollections.observableList(Arrays.asList(UserRole.values()));
private static StringConverter<UserRole> userRoleStringConverter = new StringConverter<UserRole>() {
@Override
public String toString(UserRole object) {
return object.getDescription();
}
@Override
public UserRole fromString(String string) {
return UserRole.get(string);
}
};
@Override
public void start(Stage primaryStage) throws Exception {
User fooUser = new User("Foo", UserRole.UNAUTHENTICATED);
User barUser = new User("Bar", UserRole.ADMINISTRATOR);
ApplicationInstance changeableApplicationInstance = new ApplicationInstance(fooUser, "1.0.1");
model.get().getApplicationInstances().add(new ApplicationInstance(new User("John", UserRole.DATA_ANALYSIS_GUY), "1.0.0"));
model.get().getApplicationInstances().add(new ApplicationInstance(new User("Damien", UserRole.DATA_ANALYSIS_GUY, UserRole.DATA_INPUT_GUY), "1.0.0"));
model.get().getApplicationInstances().add(changeableApplicationInstance);
VBox root = new VBox(10);
TableView<ApplicationInstance> applicationInstanceTableView = new TableView<>();
TableColumn<ApplicationInstance, String> versionColumn = new TableColumn<>("Application version");
versionColumn.setCellValueFactory(param -> param.getValue().versionProperty());
TableColumn userRoleTableColumn = new TableColumn<>("User");
TableColumn<ApplicationInstance, ObservableSet<UserRole>> userRoleColumn = new TableColumn<>("User role");
TableColumn<ApplicationInstance, String> userNameColumn = new TableColumn<>("User name");
userNameColumn.setCellValueFactory(param -> EasyBind.monadic(param.getValue().userProperty()).selectProperty(User::nameProperty));
userRoleColumn.setCellValueFactory(param -> EasyBind.monadic(param.getValue().userProperty()).selectProperty(User::userRolesProperty));
userRoleColumn.setCellFactory(param -> new TableCell<ApplicationInstance, ObservableSet<UserRole>>() {
@Override
protected void updateItem(ObservableSet<UserRole> item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
setText(getItem().stream().map(UserRole::getDescription).collect(Collectors.joining(", ")));
}
}
});
userRoleColumn.setEditable(true);
userRoleTableColumn.getColumns().addAll(userNameColumn, userRoleColumn);
applicationInstanceTableView.getColumns().addAll(versionColumn, userRoleTableColumn);
applicationInstanceTableView.itemsProperty().bind(EasyBind.monadic(model).map(Model::applicationInstancesProperty));
// Assume this action comes from somewhere else in the system
Button changeUserRoleButton = new Button("Change role of user Foo");
Random randomUserRole = new Random();
changeUserRoleButton.setOnAction(event -> {
int randomPos = randomUserRole.nextInt(UserRole.values().length);
fooUser.getUserRoles().clear();
fooUser.getUserRoles().add(UserRole.values()[randomPos]);
});
Button changeUserButton = new Button("Change user of 1.0.1 version instance");
changeUserButton.setOnAction(event -> {
if (changeableApplicationInstance.getUser() == fooUser) {
changeableApplicationInstance.setUser(barUser);
} else {
changeableApplicationInstance.setUser(fooUser);
}
});
Text onlineAdmins = new Text();
onlineAdmins.textProperty().bind(EasyBind.monadic(model).selectProperty(Model::onlineAdminsProperty).map(number -> "Online adminstrators: "+number.toString()));
root.getChildren().addAll(applicationInstanceTableView, changeUserRoleButton, changeUserButton, onlineAdmins);
Scene scene = new Scene(root, 800, 600);
root.prefWidthProperty().bind(scene.widthProperty());
primaryStage.setTitle("MCVE");
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.DECORATED);
primaryStage.show();
}
public static void main(String... args) {
launch(args);
}
private class Model {
private ListProperty<ApplicationInstance> applicationInstances = new SimpleListProperty<>(this, "applicationInstances", FXCollections.observableArrayList(
(Callback<ApplicationInstance, Observable[]>) param -> new Observable[]{
param.versionProperty(),
}
));
private IntegerProperty onlineAdmins = new SimpleIntegerProperty(this,"onlineAdmins",0);
private FilteredList<ApplicationInstance> adminInstances;
public Model() {
adminInstances = applicationInstances.filtered(applicationInstance -> (applicationInstance.getUser().getUserRoles().contains(UserRole.ADMINISTRATOR)));
adminInstances.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
onlineAdmins.set(adminInstances.size());
}
});
}
public ObservableList<ApplicationInstance> getApplicationInstances() {
return applicationInstances.get();
}
public ListProperty<ApplicationInstance> applicationInstancesProperty() {
return applicationInstances;
}
public void setApplicationInstances(ObservableList<ApplicationInstance> applicationInstances) {
this.applicationInstances.set(applicationInstances);
}
public int getOnlineAdmins() {
return onlineAdmins.get();
}
public IntegerProperty onlineAdminsProperty() {
return onlineAdmins;
}
}
private class ApplicationInstance {
ObjectProperty<User> user = new SimpleObjectProperty<>(this, "user");
StringProperty version = new SimpleStringProperty(this, "version");
public ApplicationInstance() {
}
public ApplicationInstance(User user, String version) {
this.user.set(user);
this.version.set(version);
}
public User getUser() {
return user.get();
}
public ObjectProperty<User> userProperty() {
return user;
}
public void setUser(User user) {
this.user.set(user);
}
public String getVersion() {
return version.get();
}
public StringProperty versionProperty() {
return version;
}
public void setVersion(String version) {
this.version.set(version);
}
}
public class User {
SetProperty<UserRole> userRoles = new SimpleSetProperty<>(this, "userRoles", FXCollections.observableSet());
StringProperty name = new SimpleStringProperty(this, "name");
public User() {
}
public User(String name, UserRole... userRoles) {
this.name.set(name);
Arrays.stream(userRoles).forEach(userRole -> this.userRoles.add(userRole));
}
public ObservableSet<UserRole> getUserRoles() {
return userRoles.get();
}
public SetProperty<UserRole> userRolesProperty() {
return userRoles;
}
public void setUserRoles(ObservableSet<UserRole> userRoles) {
this.userRoles.set(userRoles);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
}
// UserRights as enums for shortness
public enum UserRole {
DATA_ANALYSIS_GUY("Data analysis guy"), ADMINISTRATOR("Administrator"), SOME_ROLE("Some role"),
DATA_INPUT_GUY("Data input guy"), UNAUTHENTICATED("Unauthenticated user");
private String description;
private static Map<String, UserRole> stringUserRightMap = new HashMap<>();
static {
for (UserRole userRole : UserRole.values()) {
stringUserRightMap.put(userRole.description, userRole);
}
}
UserRole(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public static UserRole get(String description) {
return stringUserRightMap.get(description);
}
}
}
编辑:似乎没有对此的支持。使用ObservableProperty
时,直接在属性上绑定/侦听的标准方法只有在您不将它们视为对象(它们所属的bean)的更改时才有效。更有趣的是,ListProperty
获得了有一个提取器而不是SetProperty
或MapProperty
的处理方式。我真的很想知道是否有一些建筑或OOP的原因。