Это ошибка : ComboBoxListViewSkin делает очень грязные вещи, когда пытается настроить buttonCell для неконтролируемого значения. Один из виновников - updateDisplayText, который изменяет свойство text / graphi c кнопки buttonCell под ногами, то есть без прохождения updateItem:
final StringConverter<T> c = comboBox.getConverter();
final String promptText = comboBox.getPromptText();
String s = item == null && promptText != null ? promptText :
c == null ? (item == null ? null : item.toString()) : c.toString(item);
// here it sets the text - this is why the "Empty" is showing initially
cell.setText(s);
// here it unconditionally nulls the graphic - this is why the line never shows up
cell.setGraphic(null);
Чтобы взломать, нам нужна очень специальная ячейка, которая знает это неправильное поведение и делает все возможное (что, очевидно, сильно зависит от реализации, так что будьте осторожны!) справиться с ним. По сути, он должен выполнить две вещи
- , в особом случае - его состояние в updateItem, когда он находится в роли buttonCell, а значение комбо не содержится в списке
- и позаботиться его начального состояния и отменить любые начальные тряски из скина
, первые из которых зацепляются всякий раз, когда неподдерживаемое значение выбирается позднее во время жизни комбо, последнее необходимо из-за "раннего" ленивого внутреннего скина конфигурация.
Пример ячейки:
public class ShapeListCell2 extends ListCell<Shapes> {
double r = 10;
Circle circle = new Circle(r, r, r);
Line line = new Line(0, r, r*2, r);
Rectangle rect = new Rectangle(r*2, r*2);
ComboBox<Shapes> combo;
InvalidationListener gl = obs -> graphicUpdated();
/**
* Use this constructor in the combo's cellFactory.
*/
public ShapeListCell2() {
this(null);
}
/**
* Use this constructor when being the button cell.
* @param combo
*/
public ShapeListCell2(ComboBox combo) {
this.combo = combo;
if (isButtonCell()) {
// initialize with empty text/graphic
resetButtonCell();
// register listener to reset on first nulling by skin
graphicProperty().addListener(gl);
}
}
private void graphicUpdated() {
// remove listener
graphicProperty().removeListener(gl);
resetButtonCell();
}
protected void resetButtonCell() {
setText(Shapes.Empty.toString());
setGraphic(line);
}
protected boolean isButtonCell() {
return combo != null;
}
@Override
public void updateItem(Shapes item, boolean empty) {
super.updateItem(item, empty);
// special case: buttonCell with uncontained value
if (isButtonCell() && getIndex() < 0 && combo.getValue() != null) {
resetButtonCell();
return;
}
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
switch (item) {
case Circle:
setGraphic(circle);
break;
case Empty:
setGraphic(line);
break;
case Square:
setGraphic(rect);
break;
}
}
}
}
Осторожно: это хак для fx11 +, похоже, что он не помогает на fx8 в соответствии с обратной связью от OP.
Обратите внимание, что я переместил все экземпляры узла из updateItem - он не должен иметь большой нагрузки:)