Tuesday, May 10, 2016

Event handling with JavaFX ChoiceBox objects

Writing event handlers for many JavaFX components is relatively straightforward. In comparison, writing event handlers for the JavaFX ChoiceBox can be very confusing. Once you get the hang of it, though, it really is not too difficult.

For this example, we will build a JavaFX GUI containing a ChoiceBox and two TextFields. One TextField will allow the user to enter a string, which will then be placed in the ChoiceBox. The other TextField will show the length of the currently displayed String from the ChoiceBox. I'll provide the controller class below; the remainder of the interface is an exercise for the reader.

import javafx.fxml.FXML;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TextField;

public class ChoiceBoxDemoController {
 
  @FXML ChoiceBox choices;
  @FXML TextField entries;
  @FXML TextField length;
 
  @FXML
  void initialize() {
    length.setEditable(false);
  
    entries.setOnAction(event -> {
      if (entries.getText().length() > 0) {
        choices.getItems().add(entries.getText());
        entries.setText("");
        int added = choices.getItems().size() - 1;
        choices.getSelectionModel().select(added);
      }
    });
  
    choices.getSelectionModel().selectedItemProperty()
    .addListener((obs, oldV, newV) -> 
    length.setText(Integer.toString(newV.length())));
  }
}

Note that we have created two different event handlers. The handler for the TextField is very straightforward. The setOnAction() method specifies a handler for whenever the user types the Enter key on that TextField. The ChoiceBox is backed by an ObservableArrayList containing its items. This list is accessed using the getItems() method. So to stick an item on the end of the list, I just use its add() method. I also want to update the ChoiceBox to display what I just added. The index will be the last possible index (i.e., size() - 1), and the select() method of the SelectionModel will designate it. Note that calling select() will also trigger the selection event handler described below.

In contrast, setting up the ChoiceBox event handler is more involved. There are several levels of object that we must traverse before actually being able to set it up. Please note that I do not seek here to justify this design, only to explain it! First, we need to get a reference to the SelectionModel object. Next, we get a reference to the SelectedItemProperty. This property is an Observer of the currently selected item. (If you wish to work with the item index, use the SelectedIndexProperty instead.) It is to this property that we can finally attach an event handler.

The handler itself conforms to the ChangeListener interface. The changed() method takes three arguments. The first argument ("obs") is a reference to the SelectedItemProperty to which the handler is attached. I've never found a use for this. The second argument ("oldV") is the value of the selected ChoiceBox item before the event occurs. The third argument ("newV") is the value of the selected ChoiceBox item after the event occurs. This last argument, in particular, tends to be the most useful, and it is what I use here in this example. What I recommend avoiding at all costs is any reference back to the ChoiceBox object itself; I have found the program's behavior to be much more predictable using these arguments.

No comments:

Post a Comment