In javaFX 2.0 there is ChoiceBox control it is practically made of 2 items, one is label and other on is ContextMenu. Somewhere I saw somebody said something like “use ChoiceBox with low amount of data and ListView for high amount of data” but he did not said why. I was curious why not is there some limit or what, so i tried what would happened if I insert at least 100 items in Choice Box.
Lets try code example:
<br /> ChoiceBox<String> cb = new ChoiceBox<String>(FXCollections.observableArrayList(Font.getFamilies())); // just add to scene root
I need nice ChoiceBox with font list in application that am currently developing so I decided to take a little test.
When I saw result i know why someone said use it only with low data 10-20 items maximum. You can see result by yourself or on down image.
That really disappointed me i need ChoiceBox with more then 150 fonts (all system fonts + custom fonts) and i don’t want to show big ListView or huge context menu because it looks ugly. After little bit of digging testing and experimenting. I have decided to create my own ComboBox.
Some nice features of ComboBox that i want to see:
- having scrolling ListView (we all know that ListView can have CellFactory) that is great because i want to see font preview in list
- some nice animation when showing or hiding ListView
- setting size of visible ListView
- getting selectedItem from ComboBox
- …
<br />
package playground.control;</p>
<p>import java.util.List;<br />
import javafx.beans.property.BooleanProperty;<br />
import javafx.beans.property.DoubleProperty;<br />
import javafx.beans.value.InvalidationListener;<br />
import javafx.beans.value.ObservableValue;<br />
import javafx.collections.ObservableList;<br />
import javafx.scene.control.Control;<br />
import javafx.scene.control.ListCell;<br />
import javafx.scene.control.ListView;<br />
import javafx.scene.control.MultipleSelectionModel;<br />
import javafx.util.Callback;</p>
<p>/**<br />
*<br />
* @author jojorabbit<br />
*<br />
*/<br />
public class ListComboBox<T> extends Control {</p>
<p> private static final String LIST_COMBO_BOX_STYLE_CLASS = "list-combo-box";<br />
protected static final String PSEUDO_CLASS_SHOWING = "showing";<br />
protected ListView<T> listView;<br />
protected DoubleProperty visibleHeight = new DoubleProperty(0.0d);<br />
private BooleanProperty showing = new BooleanProperty() {</p>
<p> @Override<br />
protected void invalidated() {<br />
ListComboBox.this.impl_pseudoClassStateChanged(PSEUDO_CLASS_SHOWING);<br />
}<br />
};</p>
<p> public ListComboBox() {<br />
init();<br />
initListeners();<br />
visibleHeight.set(200.0d); // set default height to 200.0<br />
}</p>
<p> public boolean isShowing() {<br />
return showing.get();<br />
}</p>
<p> public BooleanProperty showingProperty() {<br />
return showing;<br />
}</p>
<p> @Override<br />
public void impl_getPseudoClassState(List<String> list) {<br />
super.impl_getPseudoClassState(list);<br />
if(isShowing()) {<br />
list.add(PSEUDO_CLASS_SHOWING);<br />
}<br />
}</p>
<p> private void init() {<br />
getStyleClass().add(LIST_COMBO_BOX_STYLE_CLASS);<br />
listView = new ListView<T>();<br />
listView.getStyleClass().add("list");<br />
}</p>
<p> public void setItems(ObservableList<T> items) {<br />
listView.setItems(items);<br />
}</p>
<p> /**<br />
* Sets visible size of wrapped ListView<br />
* @param value<br />
*/<br />
public void setVisibleHeight(double value) {<br />
visibleHeight.set(value);<br />
}</p>
<p> /**<br />
* Add cell factory to listView<br />
* @param value cellFactory<br />
*/<br />
public void setCellFactory(Callback<ListView<T>, ListCell<T>> value) {<br />
listView.setCellFactory(value);<br />
}</p>
<p> /**<br />
* Get listView selections Model<br />
* @return selection model<br />
*/<br />
public MultipleSelectionModel<T> getSelectionModel() {<br />
return listView.getSelectionModel();<br />
}</p>
<p> /**<br />
* Get selected item<br />
* @return selected item<br />
*/<br />
public T getSelectedItem() {
return listView.getSelectionModel().getSelectedItem();
}
/**
* Get selected index
* @return selected index
*/
public int getSelectedIndex() {
return listView.getSelectionModel().getSelectedIndex();
}
private void initListeners() {
visibleHeight.addListener(new InvalidationListener<Number>() {
@Override
public void invalidated(ObservableValue<? extends Number> observable) {
listView.setPrefHeight(visibleHeight.get());
listView.setMinHeight(visibleHeight.get());
listView.setMaxHeight(visibleHeight.get());
}
});
}
}
<br /> private ListView<T> listView;<br /> private Timeline timeline; // timeline for some animations<br /> private Label selectedLabel; // label<br /> private StackPane arrow; // "holder" for arrow shape<br /> private StackPane line; // "holder" for line shape<br /> private static final Duration ANIMATION_DURATION = Duration.valueOf(300.0d); // default animation duration<br />
EDIT: 28.06.2011.
<br />
.list-combo-box .arrow-container {<br />
-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
-fx-background-insets: 0 0 0 0, 0, 1, 2;<br />
-fx-background-radius: 0 5 5 0, 0 5 5 0, 0 4 4 0, 0 3 3 0; /* top right and bottom right corners are rounded*/
-fx-alignment: CENTER;<br />
-fx-padding: 10;<br />
}<br />
Some of changes in source :
<br />
StackPane arrow = new StackPane();<br />
arrow.getStyleClass().add("arrow");</p>
<p> arrowContainer = new StackPane();<br />
arrowContainer.getStyleClass().add("arrow-container");<br />
arrowContainer.getChildren().add(arrow);






