JavaFX 2.0 Custom ComboBox


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&lt;String&gt; cb = new ChoiceBox&lt;String&gt;(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
Here is code for ListComboBox Control with some useful public methods, it has pseudo class showing:
<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&lt;T&gt; 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&lt;T&gt; 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&lt;String&gt; 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&lt;T&gt;();<br />
        listView.getStyleClass().add("list");<br />
    }</p>
<p>    public void setItems(ObservableList&lt;T&gt; 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&lt;ListView&lt;T&gt;, ListCell&lt;T&gt;&gt; value) {<br />
        listView.setCellFactory(value);<br />
    }</p>
<p>    /**<br />
     * Get listView selections Model<br />
     * @return selection model<br />
     */<br />
    public MultipleSelectionModel&lt;T&gt; 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&lt;Number&gt;() {
            @Override
            public void invalidated(ObservableValue&lt;? extends Number&gt; observable) {
                listView.setPrefHeight(visibleHeight.get());
                listView.setMinHeight(visibleHeight.get());
                listView.setMaxHeight(visibleHeight.get());
            }
        });
    }
}
All methods are described in comments.
Other things i want to see in my ComboBox are nice label and some arrow pointing down, that can be done with -fx-shape region css property and a little bit of SVG knowledge.
At begging i came out with these skin variables :
<br />
private ListView&lt;T&gt; 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 />
The main problem with custom Regions is layoutChildren method, used to layout nodes in custom Region I would say that is “must to override” method if you want to accomplish nice custom layout.
There are two important and useful methods in Region class, i will just mention them for better understanding consult API documentation. Methods are layoutInArea and positionInArea.
After few hours of playing and calculating, then adding some CSS i came out with screenshot below.
Sources can be found in github repository.
Hope you like it and feel free to comment/criticize/request features/request custom nodes/report bugs, etc.

EDIT:  28.06.2011.

I have added some changes to current comboBox, some of them are removing that ugly line, adding new StackPane that holds arrow (styleclass = arrow-container), making line with css -fx-background property, removed line from layout calculations, added arrowContainer to layout calculations, adding mouse event handler only on arrow Stack, so clicking only and only on arrow stack will result “listView – pop-up” to appear.
Here are some changes added to CSS:
<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); 
And some images of control in action, well it looks much better then before with that line. If you see some <p> < br> tags in post i will correct them later.
Hope you like it.
EDIT: 07.07.2011.
Here are some new modifications to combobox it looks and works nice code is git is updated to current version. All comments and critics are welcome and if you find any other useful features fell free to contact me.
Some other features that i want to make are only focus traversing for now maybe i will find something else later.
Here are some images of control with comments.
Hope you like it.

21 responses to “JavaFX 2.0 Custom ComboBox

  1. Pingback: JavaFX links of the week, June 27 // JavaFX News, Demos and Insight // FX Experience

  2. Pingback: Java desktop links of the week, June 27 | Jonathan Giles

  3. Pingback: JavaFX links of the week, July 11 // JavaFX News, Demos and Insight // FX Experience

  4. Thanks for the update. This is a very useful new control. Just two more questions: Would it be possible to provide a default cell factory, so that you do not have to set it explicitly if you do not need any special item display? At the moment you get an NPE if you do not set a cell factory. Would it also be possible to make the speed of the animation configurable? I would like to speed it up a little.
    Many thanks
    Michael

    • Yes you are right i forgot to make default cell Factory, thanks for pointing that out.
      It is possible to make animations duration configurable, it is in TODO list. I was even thinking with different animations too but don’t have so much time to test it out.
      I will post new comment when i update it and will write changes to comment.

    • New version of Control is in git.
      Changes :
      – added animation duration property
      – added default cellFactory
      – added focus traversing (TAB, SHIFT+TAB, …)

  5. Thanks again for the update. I have a little problem with the code though. When I run the ListComboBoxTest unmodified it works in principle but very often the list display seems to hang, or just shows up and disappears immediately or just shows an empty border without content when I repeatedly click on the arrow. I found out that this depends on the setting of the animation time. The setting in the example is 10ms. If I set that to 100ms the nasty effects disappear. If I set it to 0ms the list appears but never goes away again. So there seems to be something wrong with the animation when the duration is getting small.
    Thanks again
    Michael

    • No problem. I did not have much time to play a lot with it, i will see what is problem with effect, if nothing else would not work I will limit animation duration to some value.

  6. Hi jojorabbit4,
    Just downloaded listcombobox stuff, and tried to run it after doing a clean and build on my test machine. This is what I get:

    Deleting: C:\Users\Mark\Documents\NetBeansProjects\ListComboBoxTest\build\built-jar.properties
    deps-jar:
    Updating property file: C:\Users\Mark\Documents\NetBeansProjects\ListComboBoxTest\build\built-jar.properties
    Copying 1 file to C:\Users\Mark\Documents\NetBeansProjects\ListComboBoxTest\build\classes
    compile:
    run:
    fonts: 295
    WARNING: com.sun.javafx.css.StyleManager$2 run Resource “null” not found.
    WARNING: com.sun.javafx.css.StyleManager$2 run Resource “null” not found.
    SEVERE: javafx.scene.control.Control impl_processCSS The -fx-skin property has not been defined in CSS for ListComboBox@32007142[styleClass=list-combo-box]
    SEVERE: javafx.scene.control.Control impl_processCSS The -fx-skin property has not been defined in CSS for ListComboBox@3b0bbdb3[styleClass=list-combo-box]
    SEVERE: javafx.scene.control.Control impl_processCSS The -fx-skin property has not been defined in CSS for ListComboBox@7fd3510d[styleClass=list-combo-box]
    SEVERE: javafx.scene.control.Control impl_processCSS The -fx-skin property has not been defined in CSS for ListComboBox@32007142[styleClass=list-combo-box]
    SEVERE: javafx.scene.control.Control impl_processCSS The -fx-skin property has not been defined in CSS for ListComboBox@3b0bbdb3[styleClass=list-combo-box]
    SEVERE: javafx.scene.control.Control impl_processCSS The -fx-skin property has not been defined in CSS for ListComboBox@7fd3510d[styleClass=list-combo-box]
    BUILD SUCCESSFUL (total time: 3 second)

    Eventhough I have insured that I have everything set up the same way you have it defined on github.

    • That is really old code now, i need to update github with newest code. Or you can go to jfxtras home page and run it through jfxtras it also has builder now so you can use it with FXML.

  7. disabled combobox shows a blurred text, can we increase the brightness of the text? I have tried the opacity but i can not achieve full brightness in displayed text.

  8. Hello I am so thrilled I found your weblog, I really found you by mistake, while I was looking
    on Digg for something else, Anyhow I am here now and would just like to say thank
    you for a fantastic post and a all round entertaining blog (I also love
    the theme/design), I don’t have time to go through it all at the minute but I have book-marked it and also included your RSS feeds, so when I have time I will be back to read more, Please do keep up the fantastic job.

Leave a reply to Mark Cancel reply