JavaFX 2.0, Swing 2.0?

  • Post category:Java / javafx / UI

At the beginning of my previous post I mentioned that I was stuck in the implementation of the calendar picker, because I was using one listener for 42 ToggleButtons, and I could not find out which of the 42 ToggleButtons was actually clicked. The code I have looks like this:

class Skin
{
	/**
	 * create the skin by using other controls
	 */
	private void createNodes()
	{
		for (int i = 0; i < 6 * 7; i++)
		{
			ToggleButton lToggleButton = new ToggleButton();
			lToggleButton.selectedProperty().addListener( iSelectedListener );
			...
		}
	}

	// one listener for all 42 buttons
	final private InvalidationListener<Boolean> iSelectedListener = new InvalidationListener<Boolean>()
	{
		@Override public void invalidated(ObservableValue<? extends Boolean> observableValue)
		{
			// selected or deselected
			boolean lSelected = observableValue.getValue();

			// TODO: which ToggleButton was pressed?
		}
	}
}

After having dropped this at the JavaFX people I went into pause, assuming the InvalidationListener API would be extended to include the origin of the event (like Swing events do). After some time Richard Bair came back to me and explained that extending the API with a reference to the bean would mean additional bytes for each property. My initial reaction was; so??? It’s just a few bytes? But then I realized that JavaFX is not about controls, but about graphics; controls are just a subset in which I’m very interested, but the real focus of JavaFX is much more basic and raw. There will be applications with thousands of nodes, and then a few bytes per property quickly become megabytes and the rise of the mobile devices suddenly make memory important again. So it became clear that extending the InvalidationListener API is not wise indeed.

It also became clear that a lot of API design decisions are made focused on these thousands of nodes and not focused on controls. This means that people expecting JavaFX to be a perfect replacement for Swing will have some adjusting to do; JavaFX may not have the wealth of information available at your fingertips in the way Swing does. The API’s are much leaner, cleaner and as a result less informative. In the end the solution to my problem was to bind the one thing that is unique in the listener, the observable, to the button myself using a map.

class Skin
{
	/**
	 * create the skin by using other controls
	 */
	private void createNodes()
	{
		for (int i = 0; i < 6 * 7; i++)
		{
			ToggleButton lToggleButton = new ToggleButton();
			lToggleButton.selectedProperty().addListener( iSelectedListener );

			// remember which bean belongs to this property
			iBooleanPropertyToDayToggleButtonMap.put(lToggleButton.selectedProperty(), lToggleButton);
			...
		}
	}
	final private Map<BooleanProperty, ToggleButton> iBooleanPropertyToDayToggleButtonMap = new HashMap<BooleanProperty, ToggleButton>();

	// one listener for all 42 buttons
	final private InvalidationListener<Boolean> iSelectedListener = new InvalidationListener<Boolean>()
	{
		@Override public void invalidated(ObservableValue<? extends Boolean> observableValue)
		{
			// this is the actual observable
			BooleanProperty lBooleanProperty = (BooleanProperty)observableValue;

			// which ToggleButton was pressed
			ToggleButton lToggleButton = iBooleanPropertyToDayToggleButtonMap.get(lBooleanProperty);
		}
	}
}

It may turn out that implementing extended listener API’s for controls (which usually do not run into the thousands) may become desirable in future versions of JavaFX2.

This Post Has 3 Comments

  1. Gail

    Hi Tbee,

    I realize your post is from awhile ago. But I thought I’d offer this. JavaFX properties include context information, which includes a reference to the property’s enclosing object. Therefore, in your listener code you can get a reference to the particular ToggleButton with getBean() as follows.

    public void invalidated(Observable observable) {
    // this is the actual observable
    BooleanProperty lBooleanProperty = (BooleanProperty) observable;

    // which ToggleButton was pressed
    ToggleButton lToggleButton = (ToggleButton) lBooleanProperty.getBean();
    // do something with lToggleButton
    }

    Gail

    1. tbeernot

      Hi Gail,

      Thanks for the suggestion. At the time of writing this post, this reference was not available in the event. The discussion was about memory usage and adding a reference was under debate. The solution I blogged about was the consensus.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.