JavaFX ChoiceBox添加类型安全的分隔符

问题描述 投票:4回答:3

我希望在选择框中添加分隔符,并且仍然保持类型安全。

在我看到的所有示例中,它们仅执行以下操作:

ChoiceBox<Object> cb =  new ChoiceBox<>();
cb.getItems().addAll("one", "two", new Separator(), "fadfadfasd", "afdafdsfas");

有谁想出一种能够添加分隔符并仍保持类型安全性的解决方案?

我希望,如果我想添加分隔符,我应该能够执行以下操作:

ChoiceBox<T> cb = new ChoiceBox<T>();
cb.getSeparators().add(1, new Separator()); // 1 is the index of where the separator should be

我不必为了添加分隔符而牺牲类型安全性。

javafx javafx-2 javafx-8
3个回答
0
投票

如前所述,仅当添加到项目(脏,脏)时才支持分隔符。为了按照问题中预期的方式支持他们,我们需要:

  • 将分隔符列表的概念添加到choiceBox
  • 使其皮肤知道该列表

尽管前者没什么大不了,但后者需要完全重写其皮肤(主要是c&p),因为所有内容都被严格隐藏在隐私中。如果无论如何都进行了重写,那么仅几行:-)

只是为了好玩,我正在尝试ChoiceBoxX,它解决了其选择处理中的一些讨厌的错误,因此无法抗拒尝试。

首先,为ChoiceBoxx本身添加支持:

/**
 * Adds a separator index to the list. The separator is inserted 
 * after the item with the same index. Client code
 * must keep this list in sync with the data.
 * 
 * @param separator
 */
public final void addSeparator(int separator) {
    if (separatorsList.getValue() == null) {
        separatorsList.setValue(FXCollections.observableArrayList());
    }
    separatorsList.getValue().add(separator);
};

然后在ChoiceBoxXSkin中进行一些更改

  • 必须收听分隔符列表
  • 必须期望菜单项索引!=索引项选择
  • menuItem必须保留其index-of-choiceItem

最简单的是,侦听器将重新构建弹出窗口,menuItem将dataIndex存储在其属性中,并且所有需要通过其dataIndex访问弹出窗口的代码都被委派给一个循环遍历menuItems的方法,直到找到适合的菜单为止。 :

protected RadioMenuItem getMenuItemFor(int dataIndex) {
    if (dataIndex < 0) return null;
    int loopIndex = dataIndex;
    while (loopIndex < popup.getItems().size()) {
        MenuItem item = popup.getItems().get(loopIndex);

        ObservableMap<Object, Object> properties = item.getProperties();
        Object object = properties.get("data-index");
        if ((object instanceof Integer) && dataIndex == (Integer) object) {
            return item instanceof RadioMenuItem ? (RadioMenuItem)item : null;
        }
        loopIndex++;
    }
    return null;
}

0
投票

好,您可以通过创建一个接口,然后将Separator子类化以实现此接口,来解决此问题:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Separator;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class ChoiceBoxIsSafe extends Application {

    interface FruitInterface { }

    static public class Fruit implements FruitInterface {
        private StringProperty name = new SimpleStringProperty();
        Fruit(String name) {
            this.name.set(name);
        }

        public StringProperty nameProperty() {
            return name;
        }

        @Override
        public String toString() {
            return name.get();
        }
    }

    static public class FruitySeparator extends Separator implements FruitInterface { }

    @Override
    public void start(Stage primaryStage) throws Exception {
        GridPane grid = new GridPane();
        grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(10));

        ChoiceBox<FruitInterface> cb = new ChoiceBox<>();
        cb.getItems().addAll(new Fruit("Apple"), new Fruit("Orange"), new FruitySeparator(), new Fruit("Peach"));

        Text text = new Text("");

        ReadOnlyObjectProperty<FruitInterface> selected = cb.getSelectionModel().selectedItemProperty();
        text.textProperty().bind(Bindings.select(selected, "name"));

        grid.add(cb, 0, 0);
        grid.add(text, 1, 0);

        Scene scene = new Scene(grid);
        primaryStage.setScene(scene);        
        primaryStage.show();
    }

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

但是那不是一个“优雅”的解决方案,不能在所有情况下都完成(例如ChoiceBox<String>)。

[从ChoiceBox的实现中,看起来像在ChoiceBox中将分隔符视为项目并不是一个好主意:-(。


0
投票

为了我们的其余部分:

有一种使用代码来完成此操作的简便方法(也有使用FXML进行操作的简便方法,在代码中进行操作提供了更大的灵活性)。

您只需创建一个ObservableList,然后使用您的项目(包括分隔符)将其填充,然后将该列表分配给ChoiceBox,如下所示:

private void fillChoiceBox(ChoiceBox choiceBox) {

    ObservableList items = FXCollections.observableArrayList();

    items.add("one");
    items.add("two");
    items.add("three");
    items.add(new Separator());
    items.add("Apples");
    items.add("Oranges");
    items.add("Pears");

    choiceBox.getItems().clear();
    choiceBox.getItems().addAll(items);
}
© www.soinside.com 2019 - 2024. All rights reserved.