当前位置: 首页 > 面试题库 >

装饰ObservableList并保留更改事件的最佳实践

公孙宇
2023-03-14
问题内容

我的数据源提供了一个ObservableList<String>,但是对于我的ListView我需要一个ObservableList<Warning>

A
Warning基本上只是字符串的装饰器,添加了一个布尔值以提供一种跟踪ListView复选框状态的方法,如本答案所述。

class Warning {

    private final ReadOnlyStringWrapper name;
    private final BooleanProperty checked;

    /* ... */

}

目前,我正在观察原始列表中的更改事件,并手动在警告列表中添加/删除项目:

ObservableList<String> stringList = ...;
ObservableList<Warning> warningList = ...;

stringList.addListener(new ListChangeListener<String>() {

    @Override
    public void onChanged(ListChangeListener.Change<? extends String> change) {
        if (change.wasAdded()) {
            warningList.addAll(change.getAddedSubList().stream().map(Warning::new).collect(Collectors.toList()));
        } else if (change.wasRemoved()) {
            change.getRemoved().forEach(str -> {
                warningList.removeIf(w -> str.equals(w.name));
            });
        }
    }

});

我的问题是: 有没有更优雅的方式来装饰我的String类型列表,因此可以将其用作Warning类型列表,而无需手动传递更改事件?

更准确地说:如果将字符串添加到原始列表中或从原始列表中删除,我希望立即在Warnings-list中,然后在ListView中看到此更改。


问题答案:

自您发布以来,我一直在考虑这一点。我在注释中建议的使用EasyBind无效,因为Warning每次您get(...)在映射列表上调用时,它都会创建一个新的。所以

stringList.add("foo");
warningList.get(0).setChecked(true);
assert warningList.get(0).isChecked();

会失败。

此外,如果您在源列表(stringList)中有重复的条目,则您的机制会出错(我认为),因为warningList当从中删除单个元素时,您将从删除所有对应的条目stringList。实际上,正确地删除元素非常棘手。

这是一个基于Tomas
Mikula的解决方案,MappedList它缓存了源元素和被映射元素之间的映射。它使用IdentityHashMap来确保两个列表中重复元素的行为正确。请注意,这仅适用于在将项目添加到源列表时要创建新对象的特定情况,因此,不能(也不能)代替EasyBind中的机制。

import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.transformation.TransformationList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxListCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class WrappedObjectListExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        ObservableList<String> stringList = FXCollections.observableArrayList("One", "Two", "Three");
        ObservableList<Warning> warningList = new CachingMappedList<Warning, String>(stringList, Warning::new);

        ListView<String> stringListView = new ListView<>(stringList);
        ListView<Warning> warningListView = new ListView<>(warningList);

        warningListView.setCellFactory(CheckBoxListCell.forListView(Warning::checkedProperty));

        TextField textField = new TextField();
        textField.setOnAction(e -> {
            if (! textField.getText().isEmpty()) {
                stringList.add(textField.getText());
                textField.setText("");
            }
        });

        Button remove = new Button("Remove");
        remove.setOnAction(e -> stringList.remove(stringListView.getSelectionModel().getSelectedIndex()));
        remove.disableProperty().bind(stringListView.getSelectionModel().selectedItemProperty().isNull());

        HBox lists = new HBox(10, stringListView, warningListView);
        VBox root = new VBox(10, lists, textField, remove);
        root.setPadding(new Insets(20));
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static class Warning {
        private final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
        private final BooleanProperty checked = new SimpleBooleanProperty();

        public Warning(String name) {
            this.name.set(name);
        }

        public final ReadOnlyStringProperty nameProperty() {
            return this.name.getReadOnlyProperty();
        }

        public final String getName() {
            return this.nameProperty().get();
        }

        public final BooleanProperty checkedProperty() {
            return this.checked;
        }

        public final boolean isChecked() {
            return this.checkedProperty().get();
        }

        public final void setChecked(final boolean checked) {
            this.checkedProperty().set(checked);
        }

        @Override
        public String toString() {
            return getName();
        }

    }

    public static class CachingMappedList<S,T> extends TransformationList<S, T> {

        private final Function<T, S> mapper ;

        private final IdentityHashMap<T, S> cache ;

        public CachingMappedList(ObservableList<T> source, Function<T,S> mapper) {
            super(source);
            this.mapper = mapper ;
            this.cache = new IdentityHashMap<>();
        }

        @Override
        protected void sourceChanged(Change<? extends T> c) {

            fireChange(new Change<S>(this) {

                @Override
                public boolean wasAdded() {
                    return c.wasAdded();
                }

                @Override
                public boolean wasRemoved() {
                    return c.wasRemoved();
                }

                @Override
                public boolean wasReplaced() {
                    return c.wasReplaced();
                }

                @Override
                public boolean wasUpdated() {
                    return c.wasUpdated();
                }

                @Override
                public boolean wasPermutated() {
                    return c.wasPermutated();
                }


                @Override
                public boolean next() {
                    return c.next();
                }

                @Override
                public void reset() {
                    c.reset();
                }

                @Override
                public int getFrom() {
                    return c.getFrom();
                }

                @Override
                public int getTo() {
                    return c.getTo();
                }

                @Override
                public List<S> getRemoved() {
                    List<S> removed = new ArrayList<>();
                    c.getRemoved().forEach(t -> removed.add(cache.get(t)));
                    return removed;
                }

                @Override
                public int getPermutation(int i) {
                    return c.getPermutation(i);
                }

                @Override
                protected int[] getPermutation() {
                    throw new AssertionError("Unreachable code");
                }

            });

            // clean up cache:

            c.reset();
            while (c.next()) {
                if (c.wasRemoved()) {
                    c.getRemoved().forEach(cache::remove);
                }
            }            
        }

        @Override
        public int getSourceIndex(int index) {
            return index ;
        }

        @Override
        public S get(int index) {
            return cache.computeIfAbsent(getSource().get(index), mapper);
        }

        @Override
        public int size() {
            return getSource().size();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}


 类似资料:
  • 问题内容: 我在这里看到了一些有关处理和持久保存枚举式值的最佳方法的问题/讨论(例如,持久化适用于枚举的数据,如何使用NHibernate来持久化枚举),我想问一下一般共识是什么。 特别是: 这些值应如何在代码中处理? 应该如何将它们持久保存到数据库中(作为文本/作为数字)? 不同解决方案的权衡是什么? 注意:我已将本问题中最初包含的解释移至答案。 问题答案: 我试图总结我的理解。 如果有任何更正

  • 问题内容: 假设我编写了一个装饰器,它执行了非常通用的操作。例如,它可能会将所有参数转换为特定类型,执行日志记录,实现备忘录等。 这是一个例子: 到目前为止一切都很好。但是,有一个问题。装饰的函数不保留原始函数的文档: 幸运的是,有一种解决方法: 这次,函数名称和文档是正确的: 但是仍然存在一个问题:函数签名是错误的。信息“ * args,** kwargs”几乎没有用。 该怎么办?我可以想到两个

  • 抱歉,我无法理解这个问题的现有答案,也无法理解大多数问题,因为答案是模棱两可的,答案是什么正在完成的。我做了: git clone .... 使用我最喜欢的编辑器编辑文件“文件” 添加文件 git 提交文件 我想在2点后返回美国。还有3点之前。 如果您的答案包含任何git命令的参数,请不要忘记解释这些参数(例如HEAD~32)。 我的问题似乎是一个“重复的问题”。在搜索“撤销git提交”时,我没有

  • 问题内容: 据我了解,有两种方法可以做Python装饰器,既可以使用类的,也可以定义并调用函数作为装饰器。这些方法的优点/缺点是什么?有一种首选的方法吗? 例子1 例子2 问题答案: 说每种方法是否都具有“优势”是相当主观的。 但是,如果对幕后的事物有一个很好的了解,那么自然就可以为每种场合选择最佳选择。 装饰器(谈论函数装饰器)只是一个以函数为输入参数的可调用对象。Python有其相当有趣的设计

  • 问题内容: 我正在使用hibernate作为持久层。存在于同一表中的2个实体通过单表继承策略扩展了一个超类。 我有一个ID为4的B实例。如何将此实例的类型更改为C并保留其ID(4)? 上面的代码因 有可能吗? 问题答案: Hibernate尝试使持久性尽可能地透明-这意味着它尝试遵循与普通Java对象相同的原则。现在,用Java改写您的问题,您将获得: 如何将B类的实例转换为(不兼容的)C类的实例

  • 问题内容: 在单击事件之后,我需要等待elements属性更改,然后再继续操作(click事件导致某些元素移出焦点,某些其他元素通过JS获得焦点) 在花了一些时间在Web驱动程序中寻找“ waitForAttribute”(selenium1命令)的可靠替代方法之后,我可以使用下面的代码来工作。但是我不确定这是否是最好的实现… 还有其他更好的解决方案吗? 另外,任何人都可以共享一个链接,该链接指向