【问题标题】:ChoiceBox: freeze when removing selected item from another ChoiceBoxChoiceBox:从另一个 ChoiceBox 中删除所选项目时冻结
【发布时间】:2014-08-30 15:01:42
【问题描述】:

我想使用两个提供相同选择项的选择框,但另一个选择项除外。但是,在选择了几次选项后,整个 java 进程变得没有响应。

更新:如果我使用 ComboBox 而不是 ChoiceBox,则不会出现此问题。但是,解释它发生的原因会很有趣。

我有两个选择框

@FXML private ChoiceBox<String> firstCB;
@FXML private ChoiceBox<String> secondCB;

最初具有相同的选择选项

firstCB.getItems().addAll(Charset.availableCharsets().keySet());
secondCB.getItems().addAll(Charset.availableCharsets().keySet());

和事件侦听器从其他 ChoiceBox 的选项中删除新选择并使旧选项再次可用

firstCB.getSelectionModel().selectedItemProperty()
.addListener((observable, oldVal, newVal) -> {
  secondCB.getItems().remove(newVal);
  secondCB.getItems().add(oldVal);
});

具有 JComboBoxes 和以下事件处理程序的等效 Swing 代码可以工作

firstCB.addItemListener(itemEvent -> {
  if (itemEvent.getStateChange() == ItemEvent.DESELECTED) {
    secondCB.addItem((String) itemEvent.getItem());
  } else if (itemEvent.getStateChange() == ItemEvent.SELECTED) {
    secondCB.removeItem(itemEvent.getItem());
  }
});

完整代码:

类 test.Main

public class Main extends Application {
  private Parent rootPane;
  @Override
  public void start(Stage arg0) throws Exception {
    Scene scene = new Scene(rootPane);
    arg0.setScene(scene);
    arg0.show();
  }
  @Override
  public void init() throws Exception {
    FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"));
    rootPane = loader.load();
    loader.<View>getController().init();
  }
  public static void main(String[] args) {
    launch(args);
  }
}

类 test.View

public class View {
  @FXML
  private ChoiceBox<String> firstCB;
  @FXML
  private ChoiceBox<String> secondCB;
  public void init() {
    firstCB.getItems().addAll(Charset.availableCharsets().keySet());
    secondCB.getItems().addAll(Charset.availableCharsets().keySet());
    firstCB.getSelectionModel().selectedItemProperty()
        .addListener((observable, oldVal, newVal) -> {
          System.out.printf("[%s]firstCB selection changed%n", Thread.currentThread().getName());
          secondCB.getItems().remove(newVal);
          secondCB.getItems().add(oldVal);
        });
    // removing one of the event listeners doesn't help
    secondCB.getSelectionModel().selectedItemProperty()
        .addListener((observable, oldVal, newVal) -> {
          System.out.printf("[%s]secondCB selection changed%n", Thread.currentThread().getName());
          firstCB.getItems().remove(newVal);
          firstCB.getItems().add(oldVal);
        });
  }
}

main.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="rootPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="90.0" prefWidth="180.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="test.View">
   <children>
      <ChoiceBox fx:id="firstCB" layoutX="14.0" layoutY="14.0" prefWidth="150.0" />
      <ChoiceBox fx:id="secondCB" layoutX="14.0" layoutY="52.0" prefWidth="150.0" />
   </children>
</Pane>

【问题讨论】:

  • 对我来说似乎是一个使用错误:来自choicebox api doc“ChoiceBox 用于向用户呈现一组相对较小的预定义选择” - 它没有针对处理进行优化更大的项目计数(如 ComboBox,f.i. 通过虚拟化节点)

标签: javafx javafx-8


【解决方案1】:

试试这个,对我来说很好用

firstComboBox.valueProperty().addListener((obs, oldV, newV) -> {
        secondComboBox.getItems().remove(newV);
        if(oldV!= null) secondComboBox.getItems().add(oldV);
    });

secondComboBox.valueProperty().addListener((obs, oldV, newV) -> {
        firstComboBox.getItems().remove(newV);
        if(oldV!= null) firstComboBox.getItems().add(oldV);
    });

或其他具有可观察和过滤列表的非最佳解决方案

public class Controller implements Initializable{
public ComboBox<String> firstComboBox;
public ComboBox<String> secondComboBox;

@Override
public void initialize(URL location, ResourceBundle resources) {
    ObservableList<String> list = FXCollections.observableArrayList();
    list.addAll("one", "two", "three", "four", "five");
    firstComboBox.getItems().addAll(list);
    secondComboBox.getItems().addAll(list);

    firstComboBox.valueProperty().addListener(c -> refreshList(list, secondComboBox, firstComboBox));
    secondComboBox.valueProperty().addListener(c -> refreshList(list, firstComboBox, secondComboBox));
}

private void refreshList(ObservableList<String> list, ComboBox<String> choiceBox, ComboBox<String> choiceBox2) {
    String tmp = choiceBox.getValue();
    FilteredList<String> filteredList = new FilteredList<>(list, string -> !string.equals(choiceBox2.getValue()));
    choiceBox.setItems(filteredList);
    choiceBox.setValue(tmp);
}

【讨论】:

  • 您的代码确实有效,但这似乎是由于您使用了 Combo 框而不是 Choice 框。将我的原始代码更改为使用 Combo- 而不是 ChoiceBoxes 可以解决问题;相反,将代码更改为使用 ChoiceBoxes 会导致同样的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
  • 2018-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多