【问题标题】:mirror one observableList to another将一个 observableList 镜像到另一个
【发布时间】:2015-08-30 06:53:36
【问题描述】:
  1. 在 JavaFX 中,我有一个 ObservableList 对象,并且想要另一个 ObservableList 来镜像第一个列表,但包含每个对象的字符串表示。有什么比编写自定义ListChangeListener 进行转换更简单的方法吗?我有一个StringConverter,它可以提供镜像值。

  2. 同样,给定一个ObservableList<String>,我如何创建第二个ObservableList<String>,它在索引0 处有一个常量条目,并从索引1 开始镜像第一个列表?

【问题讨论】:

    标签: javafx mirror observablelist


    【解决方案1】:

    对于第一个问题,最简单的方法是使用EasyBind 框架。那么就这么简单

    ObservableList<String> stringList = EasyBind.map(myBaseList, myConverter::toString);
    

    这是一个使用 EasyBind 的 SSCCE:

    import org.fxmisc.easybind.EasyBind;
    
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ListChangeListener.Change;
    import javafx.collections.ObservableList;
    import javafx.util.StringConverter;
    
    
    public class MappedAndTransformedListExample {
    
        public static void main(String[] ags) {
            ObservableList<Person> baseList = FXCollections.observableArrayList(
                    new Person("Jacob", "Smith", "jacob.smith@example.com"),
                    new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                    new Person("Ethan", "Williams", "ethan.williams@example.com"),
                    new Person("Emma", "Jones", "emma.jones@example.com")
            );
    
            StringConverter<Person> converter = new StringConverter<Person>() {
    
                @Override
                public String toString(Person person) {
                    return person.getFirstName() + " " + person.getLastName();
                }
    
                @Override
                public Person fromString(String string) {
                    int indexOfDelimiter = string.indexOf(' ');
                    return new Person(string.substring(0, indexOfDelimiter), 
                            string.substring(indexOfDelimiter+1), 
                            "");
                }
    
            };
    
            ObservableList<String> namesList = EasyBind.map(baseList, converter::toString);
    
            namesList.forEach(System.out::println);
    
            namesList.addListener((Change<? extends String> c) -> {
                while (c.next()) {
                    if (c.wasAdded()) {
                        System.out.println("Added "+c.getAddedSubList());
                    }
                }
            });
    
            System.out.println("\nAdding Michael to base list...\n");
            baseList.add(new Person("Michael", "Brown", "michael.brown@example.com"));
    
            namesList.forEach(System.out::println);
        }
    
    
    
        public static class Person {
            private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
            private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
            private final StringProperty email = new SimpleStringProperty(this, "email");
    
            public Person(String firstName, String lastName, String email) {
                this.firstName.set(firstName);
                this.lastName.set(lastName);
                this.email.set(email);
            }
    
            public final StringProperty firstNameProperty() {
                return this.firstName;
            }
    
            public final String getFirstName() {
                return this.firstNameProperty().get();
            }
    
            public final void setFirstName(final String firstName) {
                this.firstNameProperty().set(firstName);
            }
    
            public final StringProperty lastNameProperty() {
                return this.lastName;
            }
    
            public final java.lang.String getLastName() {
                return this.lastNameProperty().get();
            }
    
            public final void setLastName(final java.lang.String lastName) {
                this.lastNameProperty().set(lastName);
            }
    
            public final StringProperty emailProperty() {
                return this.email;
            }
    
            public final java.lang.String getEmail() {
                return this.emailProperty().get();
            }
    
            public final void setEmail(final java.lang.String email) {
                this.emailProperty().set(email);
            }
    
        }
    }
    

    如果您出于某种原因不想使用第三方框架,您可以使用TransformationList(这是 EasyBind 在后台所做的:我从那里的源代码中复制了下面的代码并对其进行了修改)。

    在上面,你会替换

    ObservableList<String> namesList = EasyBind.map(baseList, converter::toString);
    

        ObservableList<String> namesList = new TransformationList<String, Person>(baseList) {
    
            @Override
            public int getSourceIndex(int index) {
                return index ;
            }
    
            @Override
            public String get(int index) {
                return converter.toString(getSource().get(index));
            }
    
            @Override
            public int size() {
                return getSource().size();
            }
    
            @Override
            protected void sourceChanged(Change<? extends Person> c) {
                fireChange(new Change<String>(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 int getPermutation(int i) {
                        return c.getPermutation(i);
                    }
    
                    @Override
                    protected int[] getPermutation() {
                        // This method is only called by the superclass methods
                        // wasPermutated() and getPermutation(int), which are
                        // both overriden by this class. There is no other way
                        // this method can be called.
                        throw new AssertionError("Unreachable code");
                    }
    
                    @Override
                    public List<String> getRemoved() {
                        ArrayList<String> res = new ArrayList<>(c.getRemovedSize());
                        for(Person removedPerson: c.getRemoved()) {
                            res.add(converter.toString(removedPerson));
                        }
                        return res;
                    }
    
                    @Override
                    public int getFrom() {
                        return c.getFrom();
                    }
    
                    @Override
                    public int getTo() {
                        return c.getTo();
                    }
    
                    @Override
                    public boolean next() {
                        return c.next();
                    }
    
                    @Override
                    public void reset() {
                        c.reset();
                    }
                });
            }
    
    
        };
    

    对于第二个问题,您必须使用转换列表。这是一个更新的main(...) 方法,它显示了如何执行此操作。 (它同样适用于第 1 部分的第二个版本。)

    public static void main(String[] ags) {
        ObservableList<Person> baseList = FXCollections.observableArrayList(
                new Person("Jacob", "Smith", "jacob.smith@example.com"),
                new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                new Person("Ethan", "Williams", "ethan.williams@example.com"),
                new Person("Emma", "Jones", "emma.jones@example.com")
        );
    
        StringConverter<Person> converter = new StringConverter<Person>() {
    
            @Override
            public String toString(Person person) {
                return person.getFirstName() + " " + person.getLastName();
            }
    
            @Override
            public Person fromString(String string) {
                int indexOfDelimiter = string.indexOf(' ');
                return new Person(string.substring(0, indexOfDelimiter), 
                        string.substring(indexOfDelimiter+1), 
                        "");
            }
    
        };
    
        ObservableList<String> namesList = EasyBind.map(baseList, converter::toString);
    
        ObservableList<String> namesListWithHeader = new TransformationList<String, String>(namesList) {
    
            @Override
            public int getSourceIndex(int index) {
                return index - 1 ;
            }
    
            @Override
            public String get(int index) {
                if (index == 0) {
                    return "Contacts";
                } else {
                    return getSource().get(index - 1);
                }
            }
    
            @Override
            public int size() {
                return getSource().size() + 1 ;
            }
    
            @Override
            protected void sourceChanged(Change<? extends String> c) {
                fireChange(new Change<String>(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 int getPermutation(int i) {
                        return c.getPermutation(i - 1) + 1;
                    }
    
                    @Override
                    protected int[] getPermutation() {
                        // This method is only called by the superclass methods
                        // wasPermutated() and getPermutation(int), which are
                        // both overriden by this class. There is no other way
                        // this method can be called.
                        throw new AssertionError("Unreachable code");
                    }
    
                    @Override
                    public List<String> getRemoved() {
                        ArrayList<String> res = new ArrayList<>(c.getRemovedSize());
                        for(String removed: c.getRemoved()) {
                            res.add(removed);
                        }
                        return res;
                    }
    
                    @Override
                    public int getFrom() {
                        return c.getFrom() + 1;
                    }
    
                    @Override
                    public int getTo() {
                        return c.getTo() + 1;
                    }
    
                    @Override
                    public boolean next() {
                        return c.next();
                    }
    
                    @Override
                    public void reset() {
                        c.reset();
                    }
                });
            }
    
        };
    
        namesListWithHeader.forEach(System.out::println);
    
        namesListWithHeader.addListener((Change<? extends String> c) -> {
            while (c.next()) {
                if (c.wasAdded()) {
                    System.out.println("Added "+c.getAddedSubList());
                    System.out.println("From: "+c.getFrom()+", To: "+c.getTo());
                }
            }
        });
    
        System.out.println("\nAdding Michael to base list...\n");
        baseList.add(new Person("Michael", "Brown", "michael.brown@example.com"));
    
        namesListWithHeader.forEach(System.out::println);
    }
    

    【讨论】:

      【解决方案2】:

      这是 James_D 使用 Lombok 的 @Delegate 的答案的略短版本,以避免编写所有委托方法

      /**
       * A List that mirrors a base List by applying a converter on all items.
       * @param <E> item type of the target List
       * @param <F> item type of the base list
       */
      public class MirroringList<E, F> extends TransformationList<E, F> implements ObservableList<E> {
      
          /** mapping function from base list item type to target list item type */
          private final Function<F, E> converter;
      
          public MirroringList(ObservableList<? extends F> list, Function<F, E> converter) {
              super(list);
              this.converter = converter;
          }
      
          @Override
          public int getSourceIndex(int index) {
              return index;
          }
      
          @Override
          public E get(int index) {
              return converter.apply(getSource().get(index));
          }
      
          @Override
          public int size() {
              return getSource().size();
          }
      
          @Override
          protected void sourceChanged(Change<? extends F> change) {
              fireChange(new DelegatingChange(change, this));
          }
      
      
          /**
           * An implementation of {@link Change} that delegates all methods to a specified change except {@link #getRemoved()} 
           */
          private class DelegatingChange extends Change<E> implements DelegatingChangeExcluded<E> {
      
              @Delegate(excludes = DelegatingChangeExcluded.class)
              private final Change<? extends F> change;
      
              public DelegatingChange(Change<? extends F> change, MirroringList<E, F> list) {
                  super(list);
                  this.change = change;
              }
      
              @Override
              protected int[] getPermutation() {
                  return new int[0];
              }
      
              @Override
              public List<E> getRemoved() {
                  return change.getRemoved().stream()
                          .map(converter)
                          .collect(Collectors.toList());
              }
          }
      
          /**
           * This interface is only used to exclude some methods from delegated methods via Lombok's @{@link Delegate}
           * so that the compiler doesn't complain.
           */
          @SuppressWarnings("unused")
          private interface DelegatingChangeExcluded<E> {
              List<E> getRemoved();
              ObservableList<? extends E> getList();
              List<E> getAddedSubList();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-15
        • 1970-01-01
        • 2021-08-12
        • 1970-01-01
        • 1970-01-01
        • 2021-08-05
        • 1970-01-01
        相关资源
        最近更新 更多