JavaFX:渲染选定的ComboBox列表项

时间:2015-12-16 15:45:20

标签: listview combobox javafx-8 cell

在下面的测试中,所选列表项应与绿色标记一起显示。它适用于最初选择的项目。但是,当选择另一个项目时,似乎没有调用#updateItem(),这似乎是标记未正确更新的原因。

public class ComboBoxCellFactoryTest extends Application
{
  public static void main(String[] args)
  {
    Application.launch(args);
  }

  @Override
  public void start(Stage stage)
  {
    Parent content = createContent();
    Scene scene = new Scene(content, 400, 300);
    stage.setScene(scene);    
    stage.show();
  }

  public Parent createContent()
  {
    FlowPane content = new FlowPane(10, 10);

    ComboBox<String> combo = new ComboBox<String>();    
    combo.setItems(FXCollections.observableArrayList("Item 1", "Item 2", "Item 3", "Item 4"));
    combo.getSelectionModel().selectLast();
    combo.setCellFactory(new Callback<ListView<String>, ListCell<String>>()
    {
      @Override
      public ListCell<String> call(ListView<String> p)
      {
        return new ListCell<String>()
        {
          private final Rectangle rectangle;
          {
            rectangle = new Rectangle(10, 10);
          }

          @Override
          protected void updateItem(String item, boolean empty)
          {
            super.updateItem(item, empty);

            if (empty || item == null)
            {
              setText(null);
              setGraphic(null);
            }
            else
            {
              boolean selected = combo.getValue().equals(item);
              rectangle.setFill(selected ? Color.GREENYELLOW : Color.RED);
              setGraphic(rectangle);
              setText(item);
            }
          }
        };
      }
    });
    content.getChildren().add(combo);
    return content;
  }    
}

1 个答案:

答案 0 :(得分:3)

对我来说看起来像个错误。解决方法(未经过正式测试,只需快速检查!)可能是使用单元格的选定属性注册侦听器并手动强制执行updateItem,如:

{
    rectangle = new Rectangle(10, 10);
    selectedProperty().addListener((c, ov, nv) -> {
        updateItem(getItem(), getItem() == null);
    });
}

<小时/> 的更新

实际上,它似乎比简单的bug更糟糕:

  • updateItem(...)没有指定任何内容。它只包含诸如之类的模糊,可以重写以允许完全自定义单元格
  • 特别是,当被调用时,它没有指定 - 每当项目被调用时,唯一提示是Cell的api doc [updateItem]中的句子在单元格中更改。根据该设计意图,对更改单元状态的自动更新的期望 - 除了更改项目 - 将是使用错误。
  • 但是:api doc包含一个非常类似于此处用法的激励示例(除了它根据哥特选择和项目值更改文本填充的颜色) - 因此用例至少在当时是用法撰写文档
  • 激励示例根本不起作用,比仅仅在预期时不更新颜色更深层次:它在预期时更新颜色。看起来在调用updateItem时无法依赖单元格自己的属性

看到最后(和最差)子弹,运行下面的示例(这是包含在一个简单的应用程序中的激励示例),选择一个项目(请注意文本颜色未按预期更改为白色)和滚动项目-by-item:在第二页的某处,有一条不可见的线(又名:文字颜色为白色)。发生的原因是因为在updateItem与selectedIndex无关时选择了isSelected(显示在列表下方的标签中)

public class CellUpdateItemAndSelected extends Application {

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

    @Override
    public void start(Stage stage) {
        Parent content = createContent();
        Scene scene = new Scene(content, 400, 300);
        stage.setScene(scene);
        stage.show();
    }

    public Parent createContent() {
        Pane content = new VBox(10);
        ListView<Number> numberList = new ListView<>(createNumberData(20));
        // this is the motivating example from cell's api doc
        numberList.setCellFactory(cf -> {
            ListCell<Number> cell = new ListCell<Number>() {

                @Override
                protected void updateItem(Number item, boolean empty) {
                    super.updateItem(item, empty);
                    setText(item == null ? "" : item.toString());
                    if (item != null) {
                        double value = item.doubleValue();
                        setTextFill(isSelected() ? Color.WHITE
                                : value == 0 ? Color.BLACK
                                        : value < 0 ? Color.RED : Color.GREEN);
                        if (isSelected()) {
                            LOG.info("selected index/item: " + getIndex() + " / " + item);
                        }
                    }
                }

            };
            return cell;
        });

        Label selectedLabel = new Label();
        selectedLabel.textProperty().bind(numberList.getSelectionModel().selectedIndexProperty().asString());
        content.getChildren().addAll(numberList, selectedLabel);
        return content;
    }

    protected ObservableList<Number> createNumberData(int count) {
        ObservableList<Number> data = FXCollections.observableArrayList();
        for (int i = -3; i < count; i++) {
            data.add(i);
        }
        return data;
    }

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(CellUpdateItemAndSelected.class.getName());
}

更新2

工作从错误开始:补丁内核现在调用updateSelected中的updateItem(...),如

public void updateSelected(boolean selected) {
    if (selected && isEmpty()) return;
    setSelected(selected);
    // fix for JDK-8145588
    updateItem(getItem(), isEmpty());
}

必须对所有其他单元状态属性执行相同操作...