Tuesday, March 13, 2018

Loading image files in JavaFX

Loading image files into JavaFX applications is not too difficult, but there is a lot of confusing and contradictory advice out there. Even worse, SceneBuilder encodes the files in a manner that is incompatible with how they will be loaded in the Java code. So I would recommend making direct references in the Controller to the files that one is loading.

Image files are easy to load if you place them in the same package as your code. Given that, an example like the following will do the trick:

import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;

public class Controller {
 
 @FXML
 private Pane pane;
 
 private ImageView img;

 @FXML
 void initialize() {
  img = new ImageView(new Image(Controller.class.getResourceAsStream("Screen.png")));
  pane.getChildren().add(img);
 }
}

Handling Key Events in JavaFX

Handling keyboard events is typically a tricky issue in any GUI environment, because it is not always clear which component is supposed to receive those events. This is typically handled by the concept of focus. That is, a component with the focus is the component that receives those events.

I have put together a short example as to how to make this work well in JavaFX. In this example, when the mouse hovers over a component, that component receives the focus. It retains the focus until another component requests it.

The two Circle objects change their color when they have the focus and an appropriate key is pressed. The Pane responds to keystrokes by printing them out on the console.

Although it is not part of this example, it is worth mentioning that clicking on a TextField or TextArea will always give that component the focus.

The below example is a Controller. I leave the main() and FXML file as exercises for the reader.


import javafx.fxml.FXML;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

public class Controller {
 
  @FXML
  Pane pane;

  @FXML
  Circle one;
 
  @FXML
  Circle two;
 
  @FXML
  void initialize() {
    one.setOnMouseEntered(event -> one.requestFocus());
    two.setOnMouseEntered(event -> two.requestFocus());
  
    one.setOnKeyPressed(event -> decodeColor(event, one));
    two.setOnKeyPressed(event -> decodeColor(event, two));

    pane.setOnMouseEntered(event -> pane.requestFocus());
    pane.setOnKeyPressed(event -> System.out.println("typed " + event.getText()));
  }
 
  void decodeColor(KeyEvent key, Circle obj) {
    if (key.getCode() == KeyCode.R) {
      obj.setFill(Color.RED);
    }
    if (key.getCode() == KeyCode.B) {
      obj.setFill(Color.BLUE);
    }
    if (key.getCode() == KeyCode.G) {
      obj.setFill(Color.GREEN);
    }
  }
}