Java CheckBoxTableCell不使用ObjectProperty<;布尔值>;
我扩展了Java CheckBoxTableCell不使用ObjectProperty<;布尔值>;,java,javafx,Java,Javafx,我扩展了SimpleObjectProperty以创建一个自定义的延迟加载实现(),LazyLoadingObjectProperty 要将此通用实现用于布尔属性,我使用LazyLoadingObjectProperty 在我的表中,我希望将布尔属性呈现为一个复选框 然而,CheckBoxTableCell似乎只适用于BooleanProperty,而不适用于ObjectProperty 这是为什么?我该如何解决 下面是一些代码: public class ExampleTable extend
SimpleObjectProperty
以创建一个自定义的延迟加载实现(),LazyLoadingObjectProperty
要将此通用实现用于布尔属性,我使用LazyLoadingObjectProperty
在我的表中,我希望将布尔属性呈现为一个复选框
然而,CheckBoxTableCell
似乎只适用于BooleanProperty
,而不适用于ObjectProperty
这是为什么?我该如何解决
下面是一些代码:
public class ExampleTable extends Application {
private static final int NUM_ELEMENTS = 5000;
private final TableView<ExampleBean> table = new TableView<>();
private final ObservableList<ExampleBean> data = FXCollections.observableArrayList();
public static void main(final String[] args) {
launch(args);
}
@Override
public void start(final Stage stage) {
final Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
final TableColumn<ExampleBean, Boolean> c1 = new TableColumn<>("A");
c1.setCellValueFactory(new PropertyValueFactory<ExampleBean, Boolean>("p1"));
c1.setCellFactory(CheckBoxTableCell.forTableColumn(c1));
c1.setEditable(true);
c1.setPrefWidth(100);
final TableColumn<ExampleBean, String> c2 = new TableColumn<>("B");
c2.setCellValueFactory(new PropertyValueFactory<ExampleBean, String>("p2"));
c2.setCellFactory(TextFieldTableCell.forTableColumn());
c2.setEditable(true);
c2.setPrefWidth(100);
for (int i = 0; i < NUM_ELEMENTS; i++) {
data.add(new ExampleBean());
}
final ScrollPane sp = new ScrollPane();
sp.setContent(table);
sp.setMaxHeight(Double.POSITIVE_INFINITY);
sp.setMaxWidth(Double.POSITIVE_INFINITY);
sp.setFitToHeight(true);
sp.setFitToWidth(true);
table.setEditable(true);
table.setItems(data);
// table.setMaxHeight(Double.POSITIVE_INFINITY);
// table.setMaxWidth(Double.POSITIVE_INFINITY);
table.getColumns().addAll(c1, c2);
final ContextMenu cm = new ContextMenu();
cm.getItems().add(new MenuItem("bu"));
table.setContextMenu(cm);
final VBox vbox = new VBox();
vbox.setSpacing(5);
VBox.setVgrow(sp, Priority.ALWAYS);
vbox.getChildren().addAll(sp);
scene.setRoot(vbox);
stage.setScene(scene);
stage.show();
}
}
public class ExampleBean {
private ObjectProperty<Boolean> p1;
private ObjectProperty<String> p2;
public ExampleBean() {
p1 = new SimpleObjectProperty(true);
p1.addListener((o, ov, nv) -> {
System.err.println("Value changed " + ov + " -> " + nv);
});
p2 = new SimpleObjectProperty(Integer.toString(new Random().nextInt()));
p2.addListener((o, ov, nv) -> {
System.err.println("Value changed " + ov + " -> " + nv);
});
}
public final ObjectProperty<Boolean> p1Property() {
return this.p1;
}
public final ObjectProperty<String> p2Property() {
return this.p2;
}
}
还可以找到mwe。肯定是一个bug,至少在文档中是这样,但在实现中也是这样:选中的复选框和索引处的observableValue之间的自动bidi绑定(如果可能的话)是设计意图。即使在可能的情况下,实现也无法满足这一要求 快速破解是修复错误的自定义子类,类似(未正式测试,小心!):
公共静态类FixedCheckBoxTableCell扩展了CheckBoxTableCell{
@凌驾
public void updateItem(T项,布尔值为空){
checkCallback();
super.updateItem(项,空);
}
私有void checkCallback(){
if(getSelectedStateCallback()!=null)返回;
可观测值可观测值=
(observeValue)getTableColumn().getCellObservableValue(getIndex());
//由super处理
if(BooleanProperty的可观察实例)返回;
//反正我也不能绑定吗
if(!(属性的可观察实例))返回;
//如果我们有一个ObjectProperty,它不是由super处理的
setSelectedStateCallback(索引->{
ObjectProperty p=(ObjectProperty)getTableColumn().getCellObservableValue(索引);
返回BooleanProperty.BooleanProperty(p);
});
}
}
看起来像个bug(不过我需要多花点时间来确定这一点)。可能的解决方法是在模型类中定义BooleanProperty
,并将其双向绑定到ObjectProperty
。然后使用表中的BooleanProperty
。该解决方案运行良好,非常感谢@James_D同意一个错误:回调的文档签名是ObservalEvalue,实际bidi绑定到checkBox.selected,只有当它是BooleanProperty类型时才会发生。。废话:(看看checkBoxTableCell的来源:绑定发生了(或没有发生)在updateItemSource中,源代码是。文档声称它双向绑定到一个ObservalEvalue
,这显然是无稽之谈。正如James_D建议的那样,通过将ObjectProperty
绑定到类型为BooleanProperty
的helper属性来解决这个问题。当然,这是有效的方法,但你必须这样做记住对每一处财产都这样做;)就我个人而言,我更喜欢处理它坏掉的地方。。有人应该提交一个bug。。。是的,但是FixedCheckBoxTableCell
我还必须记住每次都要使用;)我也这么认为。我将提交一个bug。。谢谢大家!提交了一个bug(9052917),我会在链接可用后尽快在这里发布。kleopatra
我同意编辑机制,整个班级更像是学生的黑客;)
public abstract class LazyLoadingObjectProperty<T> extends SimpleObjectProperty<T> {
public LazyLoadingObjectProperty() {
super();
}
public LazyLoadingObjectProperty(final Object bean, final String name, final T initialValue) {
super(bean, name, initialValue);
}
public LazyLoadingObjectProperty(final Object bean, final String name) {
super(bean, name);
}
public LazyLoadingObjectProperty(final T initialValue) {
super(initialValue);
}
private boolean loaded = false;
private final ChangeListener<T> valueChangeListener = (o, ov, nv) -> {
valueExternallyUpdated(nv);
};
/**
* Is called after the background task's finished (success or failure). Override
* if needed. E.g. to bind the value afterwards.
*/
protected void afterLoaded() {
addListener(valueChangeListener);
}
/**
* Returns a {@link Task} that will calculate this Property's value in the
* background.
*
* @return a {@link Task} that will calculate this Property's value in the
* background.
*/
protected abstract Task<T> createTask();
protected T getFailedValue(final Throwable t) {
return null;
}
@Override
public T getValue() {
if (!loaded) {
startLoadingService();
}
return super.getValue();
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(final boolean loaded) {
// the loaded property has been reset manually. Remove change listener
if (this.loaded && !loaded) {
removeListener(valueChangeListener);
}
this.loaded = loaded;
}
@Override
public void setValue(final T v) {
super.setValue(v);
}
/**
* Starts the {@link Task} that will calculate this Property's value in the
* background.
*/
protected void startLoadingService() {
setLoaded(true);
final Task<T> s = LazyLoadingObjectProperty.this.createTask();
LazyLoadingThreads.getExecutorService().submit(s);
s.setOnFailed(e -> {
setValue(getFailedValue(e.getSource().getException()));
afterLoaded();
});
s.setOnSucceeded(e -> {
setValue(s.getValue());
afterLoaded();
});
}
/**
* Override this callback method to handle external value-change-events
* (not-lazily-loaded). This callback is only called, if the value is updated
* manually, i.e., not via the lazy-loading mechanism.
*
* @param nv
* the new value
*/
protected void valueExternallyUpdated(final T nv) {
// override if needed
}
}
public static class FixedCheckBoxTableCell<S, T> extends CheckBoxTableCell<S, T> {
@Override
public void updateItem(T item, boolean empty) {
checkCallback();
super.updateItem(item, empty);
}
private void checkCallback() {
if (getSelectedStateCallback() != null) return;
ObservableValue<Boolean> observable =
(ObservableValue<Boolean>) getTableColumn().getCellObservableValue(getIndex());
// handled by super
if (observable instanceof BooleanProperty) return;
// can't bidi-bind anyway
if (!(observable instanceof Property)) return;
// getting here if we have a ObjectProperty<Boolean>, that's not handled by super
setSelectedStateCallback(index -> {
ObjectProperty<Boolean> p = (ObjectProperty<Boolean>) getTableColumn().getCellObservableValue(index);
return BooleanProperty.booleanProperty(p);
});
}
}