Tuesday, October 22, 2019

Basic JavaFX projects (Part 2)

JavaFX Mouse Event Example

Here is an example of capturing mouse events in JavaFX. 

Clone this repository into Eclipse as was specified in a previous post. (Git Repository view, clone a Git Repository).  The URI is https://github.com/cajanssen/JavaFXMouseEvent.git

After the project is created, as before (previous post) do some JavaFX project specific configuration in Eclipse.  (Add the already created user library and modify the Run configuration.)

Javadocs for JavaFX classes: https://openjfx.io/javadoc/13/allclasses-index.html

Code:

package jansproj.basicfx;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class MouseEventExample extends Application
{
    public MouseEventExample()
    {
    }

    // this application waits for and reacts to mouse click events
   
    @Override
    public void start(Stage stage) throws Exception
    {
        // Stage, Scene, root Node - standard stuff common to JavaFX programs
        stage.setTitle("Mouse Event Example");
        Group root = new Group();
        Scene scene = new Scene(root);
        stage.setScene(scene);

        int canvasWidth = 1000;
        int canvasHeight = 500;
        int canvasXCenter = canvasWidth / 2;
        int canvasYCenter = canvasHeight / 2;

        Canvas canvas = new Canvas(canvasWidth, canvasHeight);
       
        // rather than create a large tree of nodes, create a tree of only
        // one node - canvas - and draw on that
        root.getChildren().add(canvas);
       
        double blackCircleRadius = 250;
        double blueCircleRadius = 200;
        double redCircleRadius = 150;
        double goldCircleRadius = 100;

        // make concentric circles
        // drawing point for strokeOval() is the upper left corner of bounding box, not the center
        GraphicsContext gc = canvas.getGraphicsContext2D();
        gc.setLineWidth(4.0);
        gc.setStroke(Color.BLACK);
        gc.strokeOval((canvasXCenter - blackCircleRadius), (canvasYCenter - blackCircleRadius), blackCircleRadius*2, blackCircleRadius*2);
         gc.setStroke(Color.BLUE);
        gc.strokeOval((canvasXCenter - blueCircleRadius), (canvasYCenter - blueCircleRadius), blueCircleRadius*2, blueCircleRadius*2);
        gc.setStroke(Color.RED);
        gc.strokeOval((canvasXCenter - redCircleRadius), (canvasYCenter - redCircleRadius), redCircleRadius*2, redCircleRadius*2);
        gc.setStroke(Color.GOLD);
        gc.strokeOval((canvasXCenter - goldCircleRadius), (canvasYCenter - goldCircleRadius), goldCircleRadius*2, goldCircleRadius*2);

       
        // attach a Mouse Click event handler to the scene
        // rather than create the object elsewhere and pass it in to the setOnMouseClicked()
        // method, define the event handler right here with
        // an anonymous inner class - common practice
        scene.setOnMouseClicked(
            new EventHandler<MouseEvent>()
            {
                public void handle(MouseEvent e)
                {
                    System.out.println("x= " + e.getX() + " y= " + e.getY());
                    double distance = Math.sqrt(((canvasXCenter - e.getX()) * (canvasXCenter - e.getX())) + (canvasYCenter - e.getY()) *(canvasYCenter - e.getY()));
                    String location = "outside";
                    if ( distance < blackCircleRadius )
                        location = "black circle";
                    if ( distance < blueCircleRadius )
                        location = "blue circle";
                    if ( distance < redCircleRadius )
                        location = "red circle";
                    if ( distance < goldCircleRadius )
                        location = "gold circle";
                    System.out.println(location);
                }
            });

        // calling show() on the Stage is a standard requirement for a JavaFX program
        stage.show();
    }

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

The tree of Nodes to be displayed is different than the previous example.  Instead of nesting multiple Node type objects within each other (FlowPane, Label, Button), there is only one Node object in the tree - Canvas - and all the drawing is done on that Node using GraphicsContext drawing method calls.

The application draws some concentric circles on the Canvas at known distances from the center.  These distances are then used by the MouseEvent EventHandler to determine where in the collection of circles the mouse click occurred.

Monday, October 14, 2019

Basic JavaFX projects (Part 1)

This is the first of some basic JavaFX example applications.

JavaFx example with normal window controls.

This example is a simple application that includes some standard controls such as Labels and Buttons.  It creates two windows, places several text Labels in one and adds a Button that will remove one of the text Labels.

Clone this repository into Eclipse as was specified in a previous post. (Git Repository view, clone a Git Repository).  The URI is https://github.com/cajanssen/TwoWindowLabelList.git

After the project is created, as before (previous post) do some JavaFX project specific configuration.  Add the already created user library and modify the Run configuration.

Javadocs for JavaFX classes: https://openjfx.io/javadoc/13/allclasses-index.html

Application code:


package jansproj.basicfx;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class TwoWindowLabelList extends Application
{

    public void init() throws Exception
    {
        System.out.println("init called");
    }

    @Override
    public void start(Stage stage) throws Exception
    {
        stage.setTitle("JavaFX ThreeApp");

        FlowPane root = new FlowPane(Orientation.VERTICAL);

        Label label1 = new Label("First Label");
        Label label2 = new Label("Second Label");
        Label label3 = new Label("Third Label");
        Label label4 = new Label("Fourth Label");
        Label label5 = new Label("Fifth Label");
        Label label6 = new Label("Sixth Label");
        Button button1 = new Button("Remove");
        button1.setOnAction(new EventHandler<ActionEvent>()
            {
                public void handle(ActionEvent event)
                   {
                       System.out.println("button pressed");
                       System.out.println(event.getTarget().toString());
                       root.getChildren().remove(label1);
                   }
               });

        root.getChildren().add(button1);
        root.getChildren().add(label1);
        root.getChildren().add(label2);
        root.getChildren().add(label3);
        root.getChildren().addAll(label4,label5,label6);
        Scene scene = new Scene(root, 400, 300);

        stage.setScene(scene);
        stage.show();

        FlowPane secondRoot = new FlowPane(Orientation.VERTICAL);
        Label secondLabel = new Label("Second Window Label");
        secondRoot.getChildren().add(secondLabel);
        Scene secondScene = new Scene(secondRoot, 200, 400);
        Stage secondStage = new Stage();
        secondStage.setScene(secondScene);
        secondStage.setX(10);
        secondStage.setY(10);
        secondStage.show();
    }

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

The first Stage (essentially the window that the application is contained within) is created by the system and handed to the application.  An additional window can be created by the application by creating an additional Stage, along with a corresponding Scene and content graph.  The Scene needs a Node of type Parent as the root of the content graph.  These items are kind of boilerplate.  Note that the root(s) in this application are of type FlowPane, whereas in the previous JavaFX example it was a simple Group object.  The FlowPane provides for some layout to be done.

The Button has an anonymous inner class for its event handler, a fairly common technique.  When pressed, it prints to the command line and removes one of the Labels added to the FlowPane, although it only can do the removal once since after the first time there is no matching object in the List.