简体   繁体   English

如何将List中的位置绑定到该元素的属性?

[英]How to bind the position in a List to an attribute of that element?

I have an ObservableList<MyElement> list = FXCollections.observableArrayList(); 我有一个ObservableList<MyElement> list = FXCollections.observableArrayList();

public class MyElement
{
  private IntegerProperty position;//with getter
  //[...]

  //somewhere in the constructor taking the list as argument
  position.bind(list.indexOf(this));
}

Now I'd like to bind MyElement.position to the actual position in the list ie if the position changes in the list (for instance drag and drop in the GUI or anything else) I want the position property to be updated automatically. 现在我想将MyElement.position绑定到列表中的实际位置,即如果列表中的位置发生变化(例如拖放GUI或其他任何内容),我希望position属性自动更新。

Is this possible? 这可能吗? Can I make a bidirectional binding between these values? 我可以在这些值之间进行双向绑定吗?

I don't know if I correctly understood your question, but I will try to answer it :-). 我不知道我是否正确理解了你的问题,但我会尽力回答:-)。

The thing is, once a ObservableList (javafx.collections) object does not store some kind of "selected" index state, why do we should bind another integer to it? 问题是,一旦ObservableList(javafx.collections)对象没有存储某种“选定”索引状态,为什么我们应该绑定另一个整数呢?

I think, in this case, your code should be responsible for store the "selected" index state and expose it to a client code. 我认为,在这种情况下,您的代码应负责存储“选定”索引状态并将其公开给客户端代码。 If this is what you are looking for, I suggest you have three attributes to deal with it: 如果这是你想要的,我建议你有三个属性来处理它:

public class ListSelection<T> {
    private ObservableList<T> items = FXCollections.observableArrayList(new ArrayList<T>());
    private ObjectProperty<T> selectedItem = new SimpleObjectProperty<>();
    private IntegerProperty selectedIndex = new SimpleIntegerProperty(0);
}

The selected element can be controlled using the selectedIndex attribute. 可以使用selectedIndex属性控制所选元素。

Then, create a bind to the selectedItem , to "automatically" update it when selectedIndex change: 然后,创建一个绑定到selectedItem ,以在selectedIndex更改时“自动”更新它:

selectedItem.bind(
    when(isEmpty(items))
        .then(new SimpleObjectProperty<T>())
        .otherwise(valueAt(items, selectedIndex))
);

Bindings should have been imported statically: Bindings应该是静态导入的:

import static javafx.beans.binding.Bindings.*;

Notice the use of method Bindings.valueAt(ObservableList<E> list, ObservableIntegerValue index) . 注意使用方法Bindings.valueAt(ObservableList<E> list, ObservableIntegerValue index) It creates a bind to the list.get(index.getValue()) element. 它创建了对list.get(index.getValue())元素的绑定。

Finally, you can use it like this: 最后,您可以像这样使用它:

ListSelection<String> selection = new ListSelection<>();
Label label = new Label();
List<String> weekDays = Arrays.asList("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");

selection.getItems().addAll(weekDays);
label.textProperty().bind(selection.selectedItemProperty());

I also suggest you to take a look to javafx.scene.control.SelectionModel class and its subclasses (eg. javafx.scene.control.SingleSelectionModel ). 我还建议您查看javafx.scene.control.SelectionModel类及其子类(例如, javafx.scene.control.SingleSelectionModel )。 Maybe, it could be easier to extend some of them. 也许,扩展其中一些可能更容易。

I don't have a bidirectional binding but if you only want the property to be updated when the position changes in the list, you can use this code: 我没有双向绑定,但如果您只想在列表中的位置更改时更新属性,则可以使用以下代码:

import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

/**
 * An integer property that's bound to the index of an item in an observable list. For example, if your list
 * is ["car", "train", "ship"], then new PositionInListProperty("train", list) will be 1. The binding is unidirectional.
 * Changing this property doesn't affect the list.
 *
 * @param <T> Type of elements in the list.
 */
public class PositionInListProperty<T> extends SimpleIntegerProperty implements ListChangeListener<T> {
    private final T item;
    private final ObservableList<T> list;

    /**
     * Creates the property and binds it to an observable list.
     * @param item The position of this item in the list is tracked by this property.
     * @param list Whenever this list changes, the value of this property is updated.
     */
    public PositionInListProperty(T item, ObservableList<T> list) {
        this.item = item;
        this.list = list;
        this.setValue(list.indexOf(item));
        list.addListener(this);
    }

    @Override
    public void onChanged(Change<? extends T> c) {
        this.setValue(list.indexOf(item));
    }

It adds itself as a listener to the list and reacts on all events. 它将自己添加为列表的监听器并对所有事件做出反应。 You can then use the property (read-only!) as normal. 然后,您可以正常使用该属性(只读!)。 The time complexity of indexOf is O(n), though, so if you have a long list, you'll want to somehow optimize this. indexOf的时间复杂度是O(n),所以如果你有一个很长的列表,你会想要以某种方式优化它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM