这是最小化绑定失效的有效方法吗?

问题描述 投票:6回答:1

我有一些复杂的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();
    }

}
java javafx concurrency javafx-8
1个回答
2
投票

我不会提前解决性能问题。测量您的应用程序以确定您是否有问题然后继续..

让我们假设您遇到了问题,有很多方法可以解决您的抽象问题。我想到了三种解决方案:

1

合并更新(Platform.runLater())以防止在您的示例中完成FX事件队列的饱和。

而且由于你只是使绑定无效,你不必担心在途中失去价值。因此,似乎(不知道完整的应用程序)这种方式应该工作。


2

使用Nodes内置行为标记区域脏(重新)布局。在某些时候,你将调用javafx.scene.Parent.requestLayout()(这是“失效”),它将在某个时候调用javafx.scene.Parent.layoutChildren()将脏属性应用于该区域。


3

以不同方式使用解决方案2:使用虚拟化布局容器。 TableViewTreeTableViewListView都使用虚拟化方法仅更新可见细胞。有关一些JDK示例,请参阅com.sun.javafx.scene.control.skin.VirtualFlowcom.sun.javafx.scene.control.skin.VirtualContainerBase,另一个例子是Flowless API

由于您没有说明任何细节,我无法再为您提供指导。但应该清楚的是,您的方法可能非常有效,还有其他方法。

© www.soinside.com 2019 - 2024. All rights reserved.