JavaFX “Missing Features” Survey Results

I recently conducted a survey asking the community to tell me which features they are missing the most in JavaFX 8. The survey has been closed by now. It received over 100 submissions from various people, various companies, various industries. Overall the participants very quite satisfied with JavaFX so I would argue that the feedback was submitted by people who really care about the platform and would like to see it evolve over time.

The following chart shows the exact distribution of the answers given by the participants when asked how satisfied they were with JavaFX.

Bildschirmfoto 2016-01-25 um 20.04.45

Categories / Issue Areas

The feature requests can be roughly grouped into these categories:

  • Table View
  • Performance
  • Quality
  • Properties
  • CSS & FXML
  • OpenGL & 3D
  • Media
  • Missing Controls
  • Missing API
  • Other

I will try to create a blog for each one of these categories over the next couple of weeks. So stay tuned!

JavaFX Real-World Apps: European Broadcasting Union

I have the privilege to present several JavaFX Real-World Apps at JavaOne this year together with Alexander Casall, who works for Saxonia Systems in Dresden. In preparation of this talk we sent out a questionnaire to our customers and partners and asked them a few questions related to their project, their application, and their use of JavaFX.

I thought it would be a good idea to publish their answers ahead of JavaOne to give attendees a chance to prepare themselves for the talk and also to maybe get some more feedback related to JavaFX projects out there, their size, and the politics surrounding their decision making process.

The first questionnaire was returned by Stephane Metairie from the European Broadcasting Union (EBU).

Logo_blue

“Members of the EBU are radio and television companies, most of which are government-owned public service broadcasters or privately owned stations with public service missions. Active Members come from as far north as Iceland and as far south as Egypt, from Ireland in the west and Azerbaijan in the east, and almost every nation from geographical Europe in between.” – Wikipedia

EBU is also known for organizing a yearly song contest for its member countries. This year it took place in Vienna.

Bildschirmfoto 2015-09-10 um 10.12.24

The application is being developed by a combined team with developers from EBU and AISO. AISO is a software development company located in Geneva. They have a very strong focus on business software development with JavaFX. Disclaimer: I am under contract with AISO.

Screen Shot 2015-09-11 at 16.51.03

The application they are developing is used for planning / scheduling broadcasting streams (TV, Radio). Resources scheduled are satellites, satellite dishes, fiber optics, etc… The application will be used by 150 concurrent users.

Below you can see a screenshot of the application. The fact that there is a timeline control shown inside of it basically gives away how I got involved into this project as an external consultant / freelancer (see FlexGanttFX).

NEOS

Here the interview questions and answers:

General Questions

What is the name of your product / project?

NEOS.

Who are your users / customers?

Internal application. Users that are in charge of planning. They “build” dynamic networks for broadcasting streams.

What is the purpose of your software? What are its benefits?

Transmission management. Build dynamic networks based on existing resources or resources rented for the case. Allow resource booking and planning.

Is the application operational? If yes, since when? If not, when do you plan to go live?

Not yet, the plan is to go live in January 2016.

How big is the budget for your project?

3 Million Dollar.

Development

How did you get the necessary JavaFX Know-How into your team? (Consultants, Internal / External training courses)?

We involved some consultant expert in JavaFX and we trained the whole team with … Internet and Oracle resources.

With which version of JavaFX did you start? 1, 2, 8?

Javafx 2.

When did you start developing the application and how long did it take?

Starting end of 2014. Took one year so far.

How many developers worked on it? In total and on the UI.

Around 15 people now. All developers work on server and client side.

How big is the application? Lines of code, Number of classes.

2000 classes.

How big is the JavaFX client? Lines of code, Number of classes.

1000 classes.

Why did you choose JavaFX as frontend technology? And very importantly: why did you not choose HTML / Web?

It’s Java ! Better ability to use desktop resources than web/html. Java applications are more stable over time than javascript (huge number of JS frameworks rise and die all the time).

Was it difficult to convince decision makers to agree on JavaFX?

Yes, I was very challenged, by managers and by developers !!! (I’m still being challenged).

What were the biggest challenges / problems / issues / bugs you faced in the JavaFX part and how did you solve them?

No worthy datatable component. Weak or incomplete component libraries. Not much JavaFx developers on the market. Bindings / Observability sometimes is tricky. Articles on the web are growing but not sufficient in some cases. No clear MVC or sort of MVC. For well industrialized projects there is no clear MVC, or this kind of pattern.

Which 3rd-party products / frameworks / tools (open source and commercial) did you use and why did you choose them?

IntelliJ.

Did you mix JavaFX and Swing code?

No.

Outlook

Would you use JavaFX again for your next project? Please elaborate why or why not. Which recommendations do you have related to JavaFX for other companies / projects?

Maybe. But It misses a sort of framework that could manage applications at a higher level (with templating, tree page, rights, css …) I think about a CMS-like framework.

Which features would you like to see being added to JavaFX?

Datatable.

Do you plan to provide a mobile version of your application or a mobile addition?

Yes.

JavaFX Tip 20: A lot to show? Use Canvas!

There seem to be two kinds of JavaFX applications: the first one is using a scene graph with nodes and CSS styling, and the second one is using a single canvas. However, it is perfectly legal to mix these two approaches. Especially when your application has to show a lot of detailed information where you would easily end up creating thousands and thousands of nodes. Even though the overall performance of JavaFX is fantastic you will most likely bring your system down to its knees when styling is required for all of these nodes (especially when styling is required over and over again because of the dynamic nature of your visualization).

For me it was an epiphany when I realized that the only way to guarantee high performance in FlexGanttFX was to use a ListView with each cell containing a canvas. Unfortunately the code of this framework is too complex to share it with you in a small blog, so I wrote a small example that illustrates the basic concepts. The image below shows the result when running the example. The data shown by the ListView covers the years of my life span with randomly generated values for each day of each year.

Bildschirmfoto 2015-06-15 um 19.25.50

The most important class is called CanvasCell. It is a specialized list view cell containing a label and a canvas. The label is used to display the year, the canvas is used to draw the chart.

import java.util.Collections;
import java.util.List;

import javafx.geometry.Pos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;

public class CanvasCell extends ListCell<YearEntry> {

	private Label yearLabel;
	private ResizableCanvas canvas;

	public CanvasCell() {
		/*
		 * Important, otherwise we will keep seeing a horizontal scrollbar.
		 */
		setStyle("-fx-padding: 0px;");

		yearLabel = new Label();
		yearLabel
		  .setStyle("-fx-padding: 10px; -fx-font-size: 1.2em; -fx-font-weight: bold;");
		StackPane.setAlignment(yearLabel, Pos.TOP_LEFT);

		/*
		 * Create a resizable canvas and bind its width and height to the width
		 * and height of the table cell.
		 */
		canvas = new ResizableCanvas();
		canvas.widthProperty().bind(widthProperty());
		canvas.heightProperty().bind(heightProperty());

		StackPane pane = new StackPane();
		pane.getChildren().addAll(yearLabel, canvas);

		setGraphic(pane);
		setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
	}

	@Override
	protected void updateItem(YearEntry entry, boolean empty) {
		if (empty || entry == null) {
			yearLabel.setText("");
			canvas.setData(Collections.emptyList());
			canvas.draw();
		} else {
			yearLabel.setText(Integer.toString(entry.getYear()));
			canvas.setData(entry.getValues());
			canvas.draw();
		}
	}

	/*
	 * Canvas is normally not resizable but by overriding isResizable() and
	 * binding its width and height to the width and height of the cell it will
	 * automatically resize.
	 */
	class ResizableCanvas extends Canvas {

		private List<Double> data = Collections.emptyList();

		public ResizableCanvas() {

			/*
			 * Make sure the canvas draws its content again when its size
			 * changes.
			 */
			widthProperty().addListener(it -> draw());
			heightProperty().addListener(it -> draw());
		}

		@Override
		public boolean isResizable() {
			return true;
		}

		@Override
		public double prefWidth(double height) {
			return getWidth();
		}

		@Override
		public double prefHeight(double width) {
			return getHeight();
		}

		public void setData(List<Double> data) {
			this.data = data;
		}

		/*
		 * Draw a chart based on the data provided by the model.
		 */
		private void draw() {
			GraphicsContext gc = getGraphicsContext2D();
			gc.clearRect(0, 0, getWidth(), getHeight());

			Stop[] stops = new Stop[] { new Stop(0, Color.SKYBLUE),
					new Stop(1, Color.SKYBLUE.darker().darker()) };
			LinearGradient gradient = new LinearGradient(0, 0, 0, 300, false,
					CycleMethod.NO_CYCLE, stops);

			gc.setFill(gradient);

			double availableHeight = getHeight() * .8;
			double counter = 0;
			for (Double value : data) {
				double x = getWidth() / 365 * counter;
				double barHeight = availableHeight * value / 100;
				double barWidth = getWidth() / 365 + 1;
				gc.fillRect(x, getHeight() - barHeight, barWidth, barHeight);
				counter++;
			}
		}
	}
}

For the data we use a very simple class that stores the year and a list of values.

import java.util.ArrayList;
import java.util.List;


/**
 * Just some fake model object.
 */
public class YearEntry {

	private int year;

	public YearEntry(int year) {
		this.year = year;
	}

	public int getYear() {
		return year;
	}

	private List<Double> values = new ArrayList<>();

	/**
	 * Stores the values shown in the chart.
	 */
	public List<Double> getValues() {
		return values;
	}
}

And the following listing shows the main class.

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Stage;

public class CanvasApp extends Application {

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

		/*
		 * Create some random data for my life span.
		 */
		ObservableList<YearEntry> data = 
			FXCollections.observableArrayList();
		for (int year = 1969; year < 2015; year++) {
			YearEntry entry = new YearEntry(year);
			for (int day = 0; day < 365; day++) {
				entry.getValues().add(Math.random() * 100);
			}
			data.add(entry);
		}

		ListView<YearEntry> listView = new ListView<>(data);
		listView.setCellFactory(param -> new CanvasCell());
		listView.setFixedCellSize(200);

		Scene scene = new Scene(listView);

		stage.setTitle("Canvas Cell");
		stage.setScene(scene);
		stage.setWidth(600);
		stage.setHeight(600);
		stage.show();
	}

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