我有一些复杂的Observable结构,这可能是也可能不是坏主意,但这不是这个问题的焦点。
这些结构的问题在于它们会产生很多由UI显示的Observable
个对象的失效。就像我所知,当JavaFX UI显示某些内容时,它会在其上注册ChangeListener
,因此任何使用延迟评估的尝试都会消失。也就是说,使observable无效似乎告诉UI它可能已经改变,这导致UI 立即请求它的值,迫使它立即进行评估。
所以,我想通过Platform.runLater()
推迟失效。
我创建了一个名为DeferredBinding
的类,它将所有内容委托给包装Binding
,但invalidate()
方法除外,它将延迟到稍后要处理的JavaFX UI线程。它似乎有效...我可以无效一百次,它似乎只是实际处理失效一次。
但是,我之前没有见过这种模式,我担心它可能属于“不错但尝试不好”的范畴。
所以,问:这是个坏主意吗?我特别关注引入依赖于Observable
的其他DeferredBinding
对象的错误。一旦Platform.runLater()
发生,它们会好吗?
package com.myapp.SAM.model.datastructures;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Binding;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
/**
* Specialized binding that defers its invalidations to the JavaFX UI thread in a throttled manner. The idea being that, if invalidate() is called many times,
* it only basically happens once (when the UI thread gets to it).
*/
public class DeferredBinding<T> implements Binding<T> {
private static final Logger logger = Logger.getLogger(DeferredBinding.class.getName());
private final Binding<T> binding;
private final AtomicBoolean pendingInvalidation = new AtomicBoolean(false);
public DeferredBinding(Binding<T> binding) {
this.binding = binding;
}
@Override
public void addListener(ChangeListener<? super T> listener) {
binding.addListener(listener);
}
@Override
public void removeListener(ChangeListener<? super T> listener) {
binding.removeListener(listener);
}
@Override
public T getValue() {
return binding.getValue();
}
@Override
public void addListener(InvalidationListener listener) {
binding.addListener(listener);
}
@Override
public void removeListener(InvalidationListener listener) {
binding.removeListener(listener);
}
@Override
public boolean isValid() {
return binding.isValid();
}
/**
* Override logic for invalidate() method to defer invalidation to runLater. Throttle the invalidations so as not to floor the JavaFX UI thread with
* multiple calls
*/
@Override
public void invalidate() {
if (pendingInvalidation.getAndSet(true) == false) {
Platform.runLater(() -> {
// Signal that the UI is processing the pending invalidation, so any additional invalidations must schedule another update.
pendingInvalidation.set(false);
binding.invalidate();
});
}
}
@Override
public ObservableList<?> getDependencies() {
return binding.getDependencies();
}
@Override
public void dispose() {
binding.dispose();
}
}
答案 0 :(得分:2)
我不会提前解决性能问题。测量您的应用程序以确定您是否有问题然后继续..
让我们假设您遇到了问题,有很多方法可以解决您的抽象问题。我想到了三个解决方案:
<强> 1 强>
合并更新(Platform.runLater()
)以防止FX事件队列饱和,就像您在示例中所做的那样。
由于您只是使绑定无效,因此您不必担心在途中失去价值。因此,似乎(不知道完整的应用程序)这种方式应该有效。
<强> 2 强>
使用Node
内置行为标记区域脏(重新)布局。在某些时候,您将调用javafx.scene.Parent.requestLayout()
(这是“失效”),这将在将来某个时候调用javafx.scene.Parent.layoutChildren()
将脏属性应用于该区域。
第3 强>
以不同方式使用解决方案2:使用虚拟化布局容器。 TableView
,TreeTableView
和ListView
都使用虚拟化方法仅更新可见单元格。
有关某些JDK示例,请参阅com.sun.javafx.scene.control.skin.VirtualFlow
和com.sun.javafx.scene.control.skin.VirtualContainerBase
,另一个示例是Flowless API。
由于您没有说明任何细节,我无法再为您提供指导。但应该清楚的是,你的方法可能会很好地工作,还有其他方法。