I am Trying to build a cinema booker, where I have to select the seats. I was thinking about make placing all my rectangles in an array so i could use it for later, when clicking on a seat it should check if left and right seats are booked. Here the array index should help me. However I cant figure out how to get to this stage. (See picture.)
Take this scenario:
You click on a rectangle (representing a seat). It changes color only it is not Red colored. So Seats[][].checkNeighbourColor or something like that.See picture
package sample;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.awt.*;
import java.util.ArrayList;
public class Main extends Application {
private int seats = 12;
private int rows = 8;
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); //gets screen resolution data
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("");
Group root = new Group();
Scene scene = new Scene(root, screenSize.getWidth()/4, screenSize.getHeight()/3, Color.WHITE);
for (int i = 0; i<= seats; i++)
{
Rectangle r = new Rectangle();
r.setFill(Color.GREEN);
r.setX(scene.getWidth()/5+i*30);
r.setY(scene.getHeight()/5);
r.setWidth(screenSize.getHeight()/80);
r.setHeight(screenSize.getHeight()/80);
root.getChildren().add(r);
for (int q = 0; q<=rows; q++)
{
Rectangle s = new Rectangle();
s.setFill(Color.GREEN);
s.setX(scene.getWidth()/5+i*30);
s.setY(scene.getHeight()/5+q*30);
s.setWidth(screenSize.getHeight()/80);
s.setHeight(screenSize.getHeight()/80);
root.getChildren().add(s);
s.setOnMouseClicked(event ->{
s.setFill(Color.BLACK);
});
}
}
primaryStage.setScene(scene);
primaryStage.show();
}
}
It's not entirely clear to me what you're asking, but can't you just do
private Rectangle[][] rectangles = new Rectangle[seats][rows];
and then just do
rectangles[i][q] = s ;
For your listener you can do
final int row = i ;
final int seat = q ;
s.setOnMouseClicked(event -> {
// check rectangles[row-1][seat] and rectangles[row+1][seat] as needed,
// checking for range of row first
});
Aside: don't mix AWT and JavaFX. Use the Screen API to get the dimension of the physical screen(s) in JavaFX.
Okay I found my mistake. It was a simple ArrayOutOfBound (The statement in my for-loops was wrong), but thank you all for your help :)
Related
I have a JavaFX application for plotting a heatmap. The app is developed with Oracle's JDK 8 on a Windows 10 machine.
I'm using xchart-3.8.0.jar library (https://knowm.org/downloads/xchart/xchart-3.8.0.zip) for plotting the data through a swing node.
Here is the code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.layout.VBox;
import javafx.scene.layout.Priority;
import javafx.embed.swing.SwingNode;
import org.knowm.xchart.HeatMapChart;
import org.knowm.xchart.HeatMapChartBuilder;
import org.knowm.xchart.XChartPanel;
import org.knowm.xchart.style.Styler;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
public class XChartExample extends Application {
#Override public void start(Stage stage) {
VBox vBox = new VBox();
stage.setTitle("Heatmap");
HeatMapChart chart =
new HeatMapChartBuilder().xAxisTitle("X").yAxisTitle("Y").theme(Styler.ChartTheme.Matlab).build();
chart.getStyler().setShowWithinAreaPoint(true);
chart.getStyler().setLegendPosition(Styler.LegendPosition.OutsideS);
chart.getStyler().setLegendLayout(Styler.LegendLayout.Horizontal);
chart.getStyler().setPlotContentSize(1);
chart.getStyler().setShowValue(true);
int[] xData = new int[5];
xData[0] = 10;
xData[1] = 20;
xData[2] = 30;
xData[3] = 40;
xData[4] = 50;
int[] yData = new int[5];
yData[0] = 10;
yData[1] = 20;
yData[2] = 30;
yData[3] = 40;
yData[4] = 50;
int[][] heatData = new int[5][5];
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 5; ++j) {
heatData[i][j] = i + j;
}
}
chart.addSeries("Basic HeatMap", xData, yData, heatData);
SwingNode swingNode = new SwingNode();
createAndSetSwingContent(swingNode, chart);
vBox.getChildren().add(swingNode);
VBox.setVgrow(swingNode, Priority.ALWAYS);
Scene scene = new Scene(vBox, 500, 350);
stage.setTitle("Demo");
stage.setScene(scene);
stage.show();
}
private void createAndSetSwingContent(SwingNode swingNode, HeatMapChart chart) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JPanel chartPanel = new XChartPanel<>(chart);
swingNode.setContent(chartPanel);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
On running the application I usually get the following black window:
On slightly dragging the window I get the window with the chart plotted:
In some cases, on launching the app the plot is displayed at once without the black screen. In this case when I resize/move the window the black screen may appear again and I have to drag the window again and so on.
The problem does not appear when the app runs on a Virtual Machine (Windows 10) with total video memory 4 MB without enabling 3D support feature. In this case the app runs correctly with no displaying problems.
Is there a way to avoid this behavior?
This program first displays a bullseye created by three different sized circles.
Once the animate me button is clicked, the function animation() will make the existing circles shrink inwards until the size of the circles is zero.
Once the user presses the button named "Press to stop", the animation will then stop. If the user presses the button again, it will then keep going from the state it was stopped from, so on so forth.
Currently, this is not working as intended. It only creates about 9 circles (including the nine circles that the program began with). I know I will need to use the action listener in order to make the program run, but I'm having a hard time in terms of the documentation of the action listener. What am I supposed to put in the parameters of the listener? If you see any other ways around this, please feel free to let me know.
package target;
import javafx.animation.ScaleTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Target extends Application
{
Circle[] cir = new Circle[7];
Button btn = new Button("Animate me!");
StackPane root = new StackPane();
public static void main(String[] args)
{
launch(args);
}
/**
* start method will create the target and the start button first
* displayed on-screen to the user
*/
#Override
public void start(Stage primaryStage)
{
root.setStyle("-fx-border-color:black;");
cir[0] = new Circle(400, 250, 200);
cir[0].setFill(Color.RED);
cir[0].setStyle("-fx-border-color:black;");
cir[1] = new Circle(315, 165, 115);
cir[1].setFill(Color.WHITE);
cir[1].setStyle("-fx-border-color:black;");
cir[2] = new Circle(230, 80, 30);
cir[2].setFill(Color.RED);
cir[2].setStyle("-fx-border-color:black;");
root.getChildren().addAll(cir[0], cir[1], cir[2]);
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
btn.setOnAction(e ->
{
animation();
btn.setText("Press to Stop");
});
}
public void animation()
{
//Timeline animation = new Timeline(
//)
ScaleTransition[] st = new ScaleTransition[7];
boolean recycleCircles = false;
st[0]= new ScaleTransition(Duration.seconds(7), cir[0]);
st[0].setToX(0.0f);
st[0].setToY(0.0f);
st[0].play();
st[1] = new ScaleTransition(Duration.seconds(5.5), cir[1]);
st[1].setToX(0.0f);
st[1].setToY(0.0f);
st[1].play();
st[2] = new ScaleTransition(Duration.seconds(4), cir[2]);
st[2].setToX(0.0f);
st[2].setToY(0.0f);
st[2].play();
// int delayInc = 1;
int delay = 1;
//will create circles (will rotate between white and red) and then add
//to scaleTransitions
//while(btn.isPressed() == false)
{
for(int i = 3; i<st.length; i++)
{
if(recycleCircles == true)
{
i = 0;
recycleCircles = false;
}
if(i % 2 == 1)
{
cir[i] = new Circle(400,250,200);
cir[i].setFill(Color.WHITE);
cir[i].setStyle("-fx-border-color:black;");
root.getChildren().add(cir[i]);
cir[i].toBack();
st[i] = new ScaleTransition(Duration.seconds(7), cir[i]);
st[i].setDelay(Duration.seconds(delay));
delay++;
st[i].setToX(0.0f);
st[i].setToY(0.0f);
st[i].play();
}
else if(i%2==0)
{
cir[i] = new Circle(400, 250, 200);
cir[i].setFill(Color.RED);
cir[i].setStyle("-fx-border-color:black;");
root.getChildren().add(cir[i]);
cir[i].toBack();
st[i] = new ScaleTransition(Duration.seconds(7), cir[i]);
st[i].setDelay(Duration.seconds(delay));
delay++;
st[i].setToX(0.0f);
st[i].setToY(0.0f);
st[i].play();
}
if(i == 6)
recycleCircles = true;
}
}
//btn.pressedProperty().addListener(listener);
btn.setOnMousePressed(event ->
{
});
btn.setOnMouseReleased(event ->
{
for(int y = 0; y<st.length;y++)
{
}
});
}
}
Not sure whether you have any specific use case with each circle. If your are using the circles only for the purpose of alternating row colors, then you can get similar effect with radial gradient's repeat option.
To the extent I understand the question, below program is what I can think of. May be this can help you.
Just to let you know, the overall effect is slightly different from your program. The main difference in effects is, your program gives an effect/impression that each circle are shrinking towards center, as the distance between each circle is always same till it shrinked completely.
My program gives the effect/.impression like the entire board is moving away from your sight till it vanishes. In my program the distance between each circle decreases proportianally till it shrinks.
import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TargetAnimation extends Application {
Button btn = new Button("Animate me!");
StackPane root = new StackPane();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
root.setPrefSize(400, 400);
root.setStyle("-fx-border-color:black;");
Circle board = new Circle();
board.setRadius(200);
board.setStyle("-fx-fill:radial-gradient(focus-angle 0deg , focus-distance 0% , center 50% 50% , radius 21% , repeat, red 44% , white 46% );-fx-stroke-width:1px;-fx-stroke:black;");
root.getChildren().addAll(board, btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
ScaleTransition transition = new ScaleTransition(Duration.seconds(7), board);
transition.setToX(0);
transition.setToY(0);
btn.setOnAction(e -> {
switch (transition.getStatus()) {
case RUNNING:
transition.pause();
break;
case PAUSED:
transition.play();
break;
default:
board.setScaleX(1);
board.setScaleY(1);
transition.playFromStart();
}
});
}
}
The code given to setOnAction is an EventHandler, which is a #FunctionalInterface with the single method handle. That means that you can give it a lambda expression instead. The method takes an argument, which is the ActionEvent of clicking the button (created for you by JavaFX), and runs the code you give it.
If you want to pause the animation, call Animation#pause, and if you want to resume it, call Animation#play. I suggest that you create a ParallelTransition with all of your ScaleTransitions as its children. Then call the above methods on the ParallelTransition in the event handler.
That means that the setup code, like naming the button and creates the animations, goes outside of the event handler.
This question already has an answer here:
JavaFX: How to connect two Nodes by a Line?
(1 answer)
Closed 7 years ago.
Trying to connect circles together in the picture with a line from the center of the circle to the other circle. The line should be shown will the mouse is being dragged.And when I release the mouse than it should not show any line at all.
This is the code.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
public class Projekt extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
//Inserting Circles
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
Circle c = new Circle(i*60+10,j*60+10,10);
c.setFill(Color.WHITE);
c.setStroke(Color.BLACK);
c.setStrokeWidth(2);
c.setStrokeType(StrokeType.OUTSIDE);
c.setOnMouseDragged((MouseEvent e)->{
Line line = new Line(c.getCenterX(),c.getCenterY(),e.getX(),e.getY());
});
pane.getChildren().add(c);
}
}
Scene scene = new Scene(pane,600,600);
primaryStage.setScene(scene);
primaryStage.setTitle("Hello World!");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
There are several ways to do this.
1-st of all you need to add your line to the pane. Because there is line creation in your code, but nothing adds this line to the pane. And you probably don't want to create new line each time, so just create some line (maybe even static one) and change coords with line.setStartX(x); line.setStartY(y); and line.setEndX(e.getX()); line.setEndY(e.getY()); on each mouse press and mouse drag events.
I think you will not be able to do this with just 1 setOnMouseDragged listener, you have to use at least some other events to set your line visible and invisible.
Here is starter code for you to play with:
public class Main extends Application {
private Line line = new Line();
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
line.setVisible(false);
pane.getChildren().add(line);
//Inserting Circles
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
double x = i * 60 + 10;
double y = j * 60 + 10;
Circle c = new Circle(x, y, 10);
c.setFill(Color.WHITE);
c.setStroke(Color.BLACK);
c.setStrokeWidth(2);
c.setStrokeType(StrokeType.OUTSIDE);
c.setOnMousePressed((MouseEvent e) -> {
line.setStartX(x);
line.setStartY(y);
});
c.setOnMouseReleased((MouseEvent e) -> {
line.setVisible(false);
});
c.setOnMouseDragged((MouseEvent e) -> {
line.setEndX(e.getX());
line.setEndY(e.getY());
line.setVisible(true);
});
pane.getChildren().add(c);
}
}
I have to create a "slot machine effect": I have a root layer an on it I have 3 Rectangles, each one in a TilePane cell. i tryed to add an event handler that should modify the rect (resizing it and rotating it) in order to change the figure that it displays. Unfortunately, my figure is never at the center fo its cell. How can I fix it?
package test;
import java.util.Random;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
/**
*
* #author bog
*/
public class Test extends Application {
StackPane root = new StackPane();
TilePane tp = new TilePane(Orientation.HORIZONTAL);
#Override
public void start(Stage primaryStage) {
//root.getChildren().add(btn);
for(int i = 0; i < 3 ; i++){
final Rectangle r = new Rectangle(100, 100, Color.GREY);
r.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
Random rnd = new Random();
int n = rnd.nextInt(5);
if(n == 0){ // horizontal line
r.setWidth(100);
r.setHeight(5);
System.out.println("Linea orizontale");
}
if(n == 1){ // vertical line
r.setWidth(5);
r.setHeight(100);
System.out.println("Linea verticale");
}
if(n == 2){ // rombo
r.getTransforms().add(new Rotate(45,50,50));
System.out.println("rombo");
}
if(n == 3){ // back-slash line
r.setWidth(5);
r.setHeight(100);
r.getTransforms().add(new Rotate(45,50,50));
System.out.println("Linea /");
}
if(n == 4){ // slash line
r.setWidth(100);
r.setHeight(5);
r.getTransforms().add(new Rotate(45,50,50));
System.out.println("Linea \\");
}
}
});
final StackPane sp = new StackPane();
sp.getChildren().add(r);
tp.getChildren().add(sp);
}
tp.setAlignment(Pos.CENTER);
root.getChildren().add(tp);
Scene scene = new Scene(root, 500, 450);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
i am not quite sure what is your problem , and right now i cant test your code , but almost every time i got alignment issues i fix them with scene builder, where i create the static components by hand , which will be rendered allright , and inside them i place the dynamic components. So your pane could be made from scene builder starting with a basic anchor pane and inside it place 3 panes with your default width, and inside them dynamically add the images you wish each time.
hope it helps...
I have an Java FX scene with a start button and several rectangles which represent the tiles of a map. I also have drawn a sphere which represents my explorer (it has to explore the map), but I am having difficulties with running the animation.
In my OnMouseClicked handler for the start button, I start an algorithm for exploring the map which changes the position of the sphere and the colors of the tiles which have been visited. The problem is that the scene won't update itself while the algorithm is running, so I only get to see how the final scene will look like (after the algorithm has stopped running). How can I force a scene update so I can see all the color changes sequentially?
Later edit:
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Test extends Application {
private static final double boxOuterSize = 50;
private static final double boxInnerSize = 48;
private static final double boxCornerRadius = 20;
private Stage applicationStage;
private Scene applicationScene;
private static double sceneWidth = 1024;
private static double sceneHeight = 800;
private static HBox container = new HBox();
private static Group root = new Group();
private Rectangle[] rectangles = new Rectangle[10];
#Override
public void start(Stage mainStage) throws Exception {
applicationStage = mainStage;
container.setSpacing(10);
container.setPadding(new Insets(10, 10, 10, 10));
try {
applicationScene = new Scene(container, sceneWidth, sceneHeight);
applicationScene.addEventHandler(EventType.ROOT,(EventHandler<? super Event>)this);
applicationScene.setFill(Color.WHITE);
} catch (Exception exception) {
System.out.println ("exception : "+exception.getMessage());
}
applicationStage.setTitle("HurtLockerRobot - Tema 3 IA");
applicationStage.getIcons().add(new Image("icon.png"));
applicationStage.setScene(applicationScene);
for(int i=0; i<10; i++) {
Rectangle r = new Rectangle();
r.setFill(Color.BLUE);
r.setX(i * boxOuterSize);
r.setY(0);
r.setWidth(boxInnerSize);
r.setHeight(boxInnerSize);
r.setArcHeight(boxCornerRadius);
r.setArcWidth(boxCornerRadius);
r.setSmooth(true);
rectangles[i] = r;
root.getChildren().add(rectangles[i]);
}
container.getChildren().add(root);
Button startButton = new Button("Start");
startButton.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
for(int i=0; i<10; i++) {
rectangles[i].setFill(Color.RED);
// TODO: some kind of scene refresh here
}
}
});
container.getChildren().add(startButton);
applicationStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Initially all the rectangles are blue. The behavior I want to obtain here is to see the rectangles changing colors sequentially. The problem is that I only get to see the end result (all the rectangles change their color at the same time).
This is an old question and it caught my eye since this is a very general issue faced by people new to JavaFX.
The problem that OP is facing is because he updates all the rectangles at once, without waiting.
OP can wait by either creating a new Thread, put the thread on sleep for an estimated seconds for every iteration of the loop and then update the color of the rectangle on JavaFX application thread by using Platform.runLater.
#Override
public void handle(Event arg0) {
new Thread(() -> {
for(int i=0; i<10; i++) {
try {
Thread.sleep(1000); // Wait for 1 sec before updating the color
} catch (InterruptedException e) {
e.printStackTrace();
}
int finalI = i;
Platform.runLater(() -> rectangles[finalI].setFill(Color.RED));// Update on JavaFX Application Thread
}
}).start();
The above snippet is more of a traditional way of doing things. If we want to use the "JavaFX" ways of doing things, we can achieve the same by using an Animation.
Below is a code snippet which will wait for x-seconds before changing the color of the rectangle. It doesn't need any extra thread since the wait is handled by PauseTransition applied for each rectangle.
startButton.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
for(int i=0; i<10; i++) {
PauseTransition pauseTransition = new PauseTransition(Duration.seconds(i));
int finalI = i;
pauseTransition.setOnFinished(event -> rectangles[finalI].setFill(Color.RED));
pauseTransition.play();
}
}
});
It creates a PauseTransition for each rectangle and depending on its index in the array rectangles, it waits for the same number of seconds before updating the color.
This is because of :
exception : Test cannot be cast to javafx.event.EventHandler
Well, I have no idea how Class cast exception came up.
Otherwise, to delay, you can use Thread.sleep().
UPDATE:
Its good to use AnimationTimer to create an animation, you don't need to refresh anything.
Here, I have done a short EG to show color rect using FillTransition.
CODE:
import javafx.animation.FillTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class NewFXMain1 extends Application {
private static final double boxOuterSize = 50;
private static final double boxInnerSize = 48;
private static final double boxCornerRadius = 20;
private Rectangle rectangles = new Rectangle();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("rect");
Button btn = new Button();
StackPane root = new StackPane();
Rectangle r = new Rectangle();
r.setFill(Color.BLUE);
r.setX(2 * boxOuterSize);
r.setY(0);
r.setWidth(boxInnerSize);
r.setHeight(boxInnerSize);
r.setArcHeight(boxCornerRadius);
r.setArcWidth(boxCornerRadius);
r.setSmooth(true);
r.localToScene(boxOuterSize, boxOuterSize);
rectangles = r;
root.getChildren().add(rectangles);
btn.setText("display");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
FillTransition ft = new FillTransition(Duration.millis(3000), rectangles, Color.RED, Color.BLUE);
ft.setCycleCount(4);
ft.setAutoReverse(true);
ft.play();
}
});
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}