JavaFX Tutorials

Tuesday, April 25, 2017

JavaFX RadioButtons and Enumerated Types

In JavaFX, RadioButtons are usually added to a ToggleGroup to make the selections mutually exclusive.  The RadioButton selection can be bound as a property to another BooleanProperty field.  That pairing also supports bindBidirectional() such that the RadioButton control can update the field and the field can update the control.

You may want to bind the RadioButton selection to an enumerated type.  Say, you have an enumeration ChoiceType with 3 values.  You want to map each of those 3 values to a RadioButton.  Moreover, you'd like to maintain a single field of ChoiceType for use in a model component.


While there are cool binding expressions that can be used on the RadioButton controls and the owning ToggleGroup, these are limited to one-way bindings.  Specifically, you can build a case statement with the When/then/otherwise construct which will convert the boolean selections to then enumerated type.

This sample program does not use JavaFX Binding and instead uses a pair of listeners.  One listener will set the RadioButtons based on the current ChoiceType.  The other listener will set the ChoiceType from the RadioButtons.

I'm using a free-form data structure associated with each control "Properties" to associated a RadioButton with a particular ChoiceType.  userData will work equally as well, but you may be using that already.


public class RBTest extends Application {

    private static String CHOICE_PROPERTY_NAME = "choice";

    enum ChoiceType { C1, C2, C3 }

    private ObjectProperty<ChoiceType> choice = new SimpleObjectProperty<>();

    @Override
    public void start(Stage primaryStage) throws Exception {

        RadioButton rb1 = new RadioButton( "Choice 1");
        rb1.getProperties().put( CHOICE_PROPERTY_NAME, ChoiceType.C1 );

        RadioButton rb2 = new RadioButton( "Choice 2" );
        rb2.getProperties().put( CHOICE_PROPERTY_NAME, ChoiceType.C2 );

        RadioButton rb3 = new RadioButton( "Choice 3" );
        rb3.getProperties().put( CHOICE_PROPERTY_NAME, ChoiceType.C3 );

        ToggleGroup tg = new ToggleGroup();
        tg.getToggles().addAll( rb1, rb2, rb3 );

        tg.selectedToggleProperty().addListener( (obs,ov,nv) -> {
            choice.set( (ChoiceType)nv.getProperties().get(CHOICE_PROPERTY_NAME) );
        });

        choice.addListener( (obs,ov,nv) -> {
            if( nv != null ) {
                tg.getToggles()
                        .stream()
                        .filter( (rb) -> rb.getProperties().get(CHOICE_PROPERTY_NAME).equals(nv) )
                        .forEach( tg::selectToggle );
            }
        });

        choice.set( ChoiceType.C3 );

        Button btn = new Button("Print Choice");
        btn.setOnAction( (evt) -> System.out.println( choice.get() ));

        VBox vbox = new VBox( rb1, rb2, rb3, btn );
        vbox.setAlignment(Pos.CENTER);
        vbox.setSpacing(10.0d);

        Scene scene = new Scene(vbox, 320, 480 );

        primaryStage.setScene( scene );
        primaryStage.show();
    }

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

1 comment:

  1. Which is exactly what JFXtras' ToggleGroupValue control does.
    http://jfxtras.org/overview.html#_togglegroupvalue

    ReplyDelete