I got a horizontal fill problem.
I got a app javafx app with a BorderPane.
In the setBottom I would like to add a info-component.
How ever this info-component extends Canvas.
But when I add it it does not fill or resize correct.
I also tried to use a Label and that too did not fill correct but in an other way.
I also tied with a TextField and that filled out and resized the area correct.
Do you guys got any ideas how to add a Canvas that fill out the area correct?
Canvas added
http://snag.gy/seswj.jpg
Label added:
http://snag.gy/6wSy8.jpg
TextField added:
http://snag.gy/7ajUc.jpg
Best regards
Fredrik
Okey here is some code.
My main class:
package a.app;
import java.net.URL;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class BannerTest extends Application
{
private Pane mainPanel;
Stage primaryStage;
#Override
public void start(Stage ps) throws Exception
{
primaryStage = ps;
primaryStage.setTitle( "BannerTest" );
mainPanel = createBorderPaneGui();
Scene scene = new Scene(mainPanel, 700, 700);
URL resource = BannerTest.class.getResource("/BannerTest.css");
String externalForm = resource.toExternalForm();
scene.getStylesheets().add( externalForm );
primaryStage.setScene( scene );
primaryStage.setTitle("BannerTest");
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
private Pane createBorderPaneGui()
{
mainPanel = new BorderPane();
GridPane applicationPanel = getApplicationPanel();
BorderedTitledPane borderedTitledPane = new BorderedTitledPane("Hubba", applicationPanel);
((BorderPane)mainPanel).setCenter( borderedTitledPane );
BorderedTitledPane banner = getBanner( 20, mainPanel );
((BorderPane)mainPanel).setBottom( banner );
return mainPanel;
}
private GridPane getApplicationPanel()
{
GridPane applicationPanel = new GridPane();
Label direcoryLabel = new Label("Directory:");
applicationPanel.add(direcoryLabel, 0, 0);
final TextField fileTextField = new TextField();
fileTextField.setEditable(false);
applicationPanel.add(fileTextField, 1, 0);
Button button = new Button("Select more then all you can select, Select more then all you can select");
HBox hBox = new HBox(10);
hBox.setAlignment(Pos.BOTTOM_RIGHT);
hBox.getChildren().add(button);
applicationPanel.add(hBox, 2, 0);
Label infoLabel = new Label("?");
applicationPanel.add(infoLabel, 3, 0);
infoLabel.setTooltip( new Tooltip("Select directory as search root."));
applicationPanel.setGridLinesVisible(true);
return applicationPanel;
}
private BorderedTitledPane getBanner(int height, Pane panel )
{
HBox bannerBox = new HBox(10);
Banner banner = new Banner(height, "", "C:/a/image/image.png", bannerBox);
//Canvas banner = new Canvas();
//Label banner = new Label("Hola");
//TextField banner = new TextField();
bannerBox.setHgrow(banner, Priority.ALWAYS);
bannerBox.getChildren().addAll( banner );
BorderedTitledPane borderedTitledPane = new BorderedTitledPane("Info", bannerBox);
return borderedTitledPane;
}
}
This class uses a style-css:
.mainPanel {
-fx-background-color: #8fbc8f;
}
.applicationPanel {
-fx-padding: 20 20 20 20;
-fx-alignment: top-center;
-fx-background-color: #00FFFF;
-fx-border-color: #FF0000;
-fx-border-width: 20px;
}
.bannerBox {
-fx-alignment: bottom-center;
-fx-background-color: #FFFFFF;
-fx-border-color: #0000FF;
-fx-border-width: 20px;
}
.banner{
-fx-alignment: bottom-center;
-fx-background-color: #00FF00;
}
.label {
-fx-font: 14px Helvetica;
}
.bordered-titled-title {
-fx-background-color: white;
-fx-translate-y: -15;
-fx-translate-x: 10;
}
.bordered-titled-border {
-fx-content-display: top;
-fx-border-insets: 40 40 15 15;
-fx-background-color: white;
-fx-border-color: black;
-fx-border-width: 2;
}
.bordered-titled-content {
-fx-padding: 26 26 26 26;
}
As you can see my main purpose is to display a banner, this banner is a Canvas that extends this class:
package a.app;
import javafx.beans.InvalidationListener;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class ResizableWidthCanvas extends Canvas
{
private Double endValue;
public ResizableWidthCanvas(int height)
{
setHeight( height );
InvalidationListener listener = new InvalidationListener(){
public void invalidated(javafx.beans.Observable arg0) {
setEndValue( getWidth() );
draw();
}
};
widthProperty().addListener(listener);
}
private void draw() {
double width = getWidth()-4;
double height = getHeight()-4;
GraphicsContext gc = getGraphicsContext2D();
gc.clearRect(2, 2, width, height);
}
#Override
public boolean isResizable() {
return true;
}
/*
#Override
public double prefWidth(double width) {
return getWidth();
}
*/
#Override
public double prefHeight(double height) {
return getHeight();
}
public Double getEndValue()
{
return endValue;
}
public void setEndValue(Double endValue)
{
this.endValue = endValue;
}
}
The Banner class it self:
package a.app;
import java.io.File;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.util.Duration;
public class Banner extends /*Canvas*/ ResizableWidthCanvas
{
String imageUrl;
String imagePath;
DoubleProperty doublePropertyX = new SimpleDoubleProperty();
DoubleProperty doublePropertyY = new SimpleDoubleProperty();
public Banner(int height, String imageUrl, String imagePath, Pane pane)
{
super(height);
setHeight( height );
widthProperty().bind( pane.widthProperty() );
File file = new File(imagePath);
final Image image = new Image(file.toURI().toString());
Double endValue = new Double( pane.getWidth()-image.getWidth() );
KeyValue keyValue = new KeyValue( doublePropertyX, endValue );
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0),
new KeyValue(doublePropertyX, 0)
),
new KeyFrame(Duration.seconds(3),
keyValue
)
);
timeline.setAutoReverse(true);
timeline.setCycleCount(Timeline.INDEFINITE);
AnimationTimer timer = new AnimationTimer()
{
int r = 0;
int g = 0;
int b = 0;
#Override
public void handle(long now)
{
GraphicsContext gc = getGraphicsContext2D();
if(r < 255)
{
r++;
}
else if(g < 255)
{
g++;
}
else if(b < 255)
{
b++;
}
else
{
r = 0;
g = 0;
b = 0;
}
gc.setFill( Color.rgb(r,g,b) );
gc.fillRect(0, 0, getWidth(), getHeight());
gc.drawImage(image,
doublePropertyX.doubleValue(),
doublePropertyY.doubleValue()+1
);
}
};
timer.start();
timeline.play();
}
}
I also use a BorderTitelPane that looks like:
package a.app;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
public class BorderedTitledPane extends StackPane
{
public BorderedTitledPane(String titleString, Node content)
{
Label title = new Label(" " + titleString + " ");
title.getStyleClass().add("bordered-titled-title");
StackPane.setAlignment(title, Pos.TOP_LEFT);
StackPane contentPane = new StackPane();
content.getStyleClass().add("bordered-titled-content");
contentPane.getChildren().add(content);
getStyleClass().add("bordered-titled-border");
getChildren().addAll(title, contentPane);
}
}
Hope that was all.
The width of your canvas is exactly equal to its container. Its coming out of box because it's starting x position is different. This is happening because in the class Banner, inside the constructor you have mentioned
widthProperty().bind(pane.widthProperty());
This means whatever is the width of the pane that will be set as the width of the canvas.
Instead of this, write the following
pane.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
setWidth((double)t1 - 70);//You can change the number according to difference in the x position
}
});
Related
I have the main window (mainWindow.fxml) on top of which I want to display a transparent popup window (errorDialog.fxml) with 50% opacity so that the main window's content can still be seen underneath.
However, my attempts at making the background colour of overlay inside errorDialog.fxml transparent only results in the background colour being displayed as a solid 50% grey that hides the main window completely.
I tried to set the transparency both in the style attribute of "overlay" as well as in in the initialize method of controllerErrorDialog.java.
Any help is appreciated!
controllerMainWindow.java
package myPackage;
import [...];
public class controllerMainWindow extends AbstractController
{
#FXML
private Button btnOpenPopup;
#FXML
private BorderPane paneMainWindow;
//---------------------------------------------------------------------------------------------------
public void initialize()
{
}
//---------------------------------------------------------------------------------------------------
#FXML
public void handleButtonAction(ActionEvent event)
{
try {
if (event.getSource().equals(btnOpenPopup)) {
FXMLLoader errorLoader = new FXMLLoader();
errorLoader.setLocation(getClass().getResource("errorDialog.fxml"));
controllerErrorDialog errorController = new controllerErrorDialog();
errorLoader.setController(errorController);
Parent layout;
layout = errorLoader.load();
Scene errorScene = new Scene(layout);
Stage errorStage = new Stage();
errorStage.initStyle(StageStyle.UNDECORATED);
errorStage.setMaximized(true);
errorController.setStage(errorStage);
if(this.main!=null) {
errorStage.initOwner(main.getPrimaryStage());
}
errorStage.initModality(Modality.APPLICATION_MODAL);
errorStage.setScene(errorScene);
errorStage.showAndWait();
}
}catch (IOException exceptionCockpitSettings) {
System.out.println("Error when switching to cockpitSettings.");
exceptionCockpitSettings.printStackTrace();
return;
}
}
//---------------------------------------------------------------------------------------------------
}
controllerErrorDialog.java
package myPackage;
import [...];
public class controllerErrorDialog extends AbstractController implements Initializable
{
#FXML
private BorderPane overlay;
private Stage stage = null;
//-------------------------------------------------------------------------------------------------------
#Override
public void initialize(URL url, ResourceBundle rb)
{
overlay.setStyle("fx-background-color: transparent");
}
//---------------------------------------------------------------------------------------------------
public void setStage(Stage stage) {
this.stage = stage;
}
//---------------------------------------------------------------------------------------------------
}
errorDialog.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import [...]?>
<BorderPane fx:id="overlay" prefWidth="1920" prefHeight="1080" style="-fx-background-color: rgba(0,0,0,0.5)" xmlns:fx="http://javafx.com/fxml">
<top></top>
<left></left>
<center></center>
<right></right>
<bottom></bottom>
</BorderPane>
You need to make sure:
The scene has a transparent background, with errorScene.setFill(Color.TRANSPARENT);
The stage is transparent, using errorStage.initStyle(StageStyle.TRANSPARENT);
Any content (that you explicitly want to see through) other than the root has fully transparent background
Here's a complete (albeit not very user-friendly) example:
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Random;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
/**
* JavaFX App
*/
public class App extends Application {
private Random rng = new Random();
#Override
public void start(Stage stage) {
Button createError = new Button("Try something dangerous");
createError.setOnAction(e -> {
try {
throw new Exception("Boom!");
} catch (Exception exc) {
BorderPane root = new BorderPane();
root.setTop(new Label("Error"));
Label stackTrace = new Label();
StringWriter sw = new StringWriter();
exc.printStackTrace(new PrintWriter(sw));
ScrollPane scroller = new ScrollPane(stackTrace);
stackTrace.setText(sw.toString());
root.setCenter(scroller);
Button close = new Button("Close");
HBox buttons = createHBox(close);
root.setBottom(buttons);
Scene errorScene = new Scene(root, 400, 400);
errorScene.setFill(Color.TRANSPARENT);
errorScene.getStylesheets().add(getClass().getResource("transparent.css").toExternalForm());
Stage errorStage = new Stage();
close.setOnAction(evt -> errorStage.close());
errorStage.setScene(errorScene);
errorStage.initStyle(StageStyle.TRANSPARENT);
errorStage.initModality(Modality.APPLICATION_MODAL);
errorStage.initOwner(stage);
errorStage.show();
}
});
HBox buttons = createHBox(createError);
BorderPane root = new BorderPane(createContent());
root.setBottom(buttons);
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.show();
}
private HBox createHBox(Node... content) {
HBox buttons = new HBox(content);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(new Insets(2));
return buttons;
}
private Node createContent() {
Pane pane = new Pane();
for (int i = 0 ; i < 15 ; i++) {
Circle circle = new Circle(
50 + rng.nextDouble() * 500,
50 + rng.nextDouble() * 500,
50 + rng.nextDouble() * 50,
randomColor());
pane.getChildren().add(circle);
}
return pane ;
}
private Color randomColor() {
return Color.color(rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), 0.5 + 0.25 * rng.nextDouble());
}
public static void main(String[] args) {
launch();
}
}
with transparent.css being:
.root {
-fx-background-color: #ffffff7f ;
}
.root HBox, .root .scroll-pane, .root .scroll-pane .viewport {
-fx-background-color: transparent ;
}
I have to do a game (Connect 4) as a project for my university, based on an MVC(Model, View, Controller) model. Now I am done with the two views (one which asks the user for input, the other that should print out the grid for the game, based on the user's input, but I have a problem, I cannot merge these two views and the two CSS documents together. Does anybody know how to do this?
Thank you very much for your help!
import java.util.InputMismatchException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Labeled;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
public class View2 extends Application implements EventHandler<ActionEvent>{
Stage window;
//Boolean variable to store the answer
static char number;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
//GridPane
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(8);
grid.setHgap(10);
//Row Label - Constraints use (child, column, row)
Label rowLabel = new Label("Number of Rows:");
GridPane.setConstraints(rowLabel, 0, 0);
//Column Label
Label colLabel = new Label("Number of Columns:");
GridPane.setConstraints(colLabel, 0, 1);
//Connections Label
Label conLabel = new Label("Needed Connections:");
GridPane.setConstraints(conLabel, 0, 2);
//Rows Input
TextField rowInput = new TextField();
rowInput.setPromptText("Number Rows");
GridPane.setConstraints(rowInput, 1, 0);
//Column Input
TextField colInput = new TextField();
colInput.setPromptText("Number Columns");
GridPane.setConstraints(colInput, 1, 1);
//Needed Connections
TextField conInput = new TextField();
conInput.setPromptText("Needed Connections");
GridPane.setConstraints(conInput, 1, 2);
//Display customized grid
Button displayButton = new Button("Display Grid");
GridPane.setConstraints (displayButton, 1, 4);
//Checking Input
displayButton.setOnAction(e-> {
isInt(rowInput, rowInput.getText());
isInt(colInput, colInput.getText());
isInt(conInput, conInput.getText());
});
//Add everything to grid
grid.getChildren().addAll(rowLabel, colLabel, conLabel, rowInput, colInput, conInput, displayButton);
Scene scene = new Scene(grid, 400, 200);
scene.getStylesheets().add(getClass().getResource("styleView2.css").toExternalForm());
window.setScene(scene);
window.show();
}
//Check input
private boolean isInt(TextField input, String message) {
try {
int number=Integer.parseInt(input.getText());
input.setText("The input is valid");
return true;
} catch(NumberFormatException e) {
input.setText("The input is not valid");
}
return false;
}
}
//CSS for View
.root{
-fx-background-color: #383838;
-fx-font-size: 40pt;
}
.button{
-fx-background-color: linear-gradient(#86c1b9, #7cafc2);
-fx-background-radius: 15;
-fx-min-width: 120;
-fx-min-height: 120;
-fx-pref-width: 120;
-fx-pref-height: 120;
}
//CSS for View2
.root{
-fx-background-color: #F8F8F8;
-fx-font-size: 10pt;
}
.label{
-fx-text-fill: #181818;
}
.button{
-fx-background-color: #AB4642;
-fx-text-fill: #FFFFFF;
-fx-background-radius: 5;
}
import java.util.ArrayList;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
//View for creating the grid
public class View {
private Stage stage;
private Model model;
public int hRows = 6;
public int vRows = 7;
ArrayList[] hArray = new ArrayList[hRows];
public View(Stage stage, Model model) {
this.stage = stage;
this.model = model;
GridPane root = new GridPane();
stage.setTitle("Connect 4");
for (int i = 0; i < hArray.length; i ++) {
hArray[i] = new ArrayList<Button>();
}
for (int i = 0; i < hArray.length; i ++) {
for(int j = 0; j < vRows; j ++ ) {
Button b = new Button();
hArray[i].add(b);
b.setOnAction(this::gameButtons);
}
}
for (int i = 0; i < hArray.length; i ++) {
for(int j = 0; j < vRows; j ++ ) {
root.add((Node) (hArray[i].get(j)), i, j);
}
}
// Standard stuff for Scene and Stage
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
public void start() {
stage.show();
}
private void gameButtons(ActionEvent e) {
}
}
When you need to learn something new, it is often much easier to do it on a smaller scale, rather than on you application.
This goes in line with SO requirement of posting mre when asking or answering.
Your question is essentially how to change scene in a javafx app, and the following code demonstrates just that, and nothing else. To test it you may copy the entire code into one file (View2.java) and run:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class View2 extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
Button displayButton = new Button("Display Grid");
//Change scene
displayButton.setOnAction(e-> {
Model model = new Model(6,12);
View view = new View(model);
primaryStage.setScene(new Scene(view.getRoot()));
});
grid.getChildren().add(displayButton);
Scene scene = new Scene(grid, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class View{
private final GridPane root;
public View(Model model) {
root = new GridPane();
for (int i = 0; i < model.gethRows(); i ++) {
for(int j = 0; j < model.getvRows(); j ++ ) {
Button b = new Button(i+"-"+j);
root.add(b, j, i);
}
}
}
GridPane getRoot() {
return root;
}
}
class Model {
private final int hRows, vRows;
Model(int hRows, int vRows) {
this.hRows = hRows;
this.vRows = vRows;
}
int gethRows() {
return hRows;
}
int getvRows() {
return vRows;
}
}
I want image slides from right to left then it reverse from left to right using JavaFX image views.
I want this type of slides:
Here is my code, which is not giving me accurate results. It slides from left to right.
Only.java:
package com.valid;
import javafx.animation.FadeTransition;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.SequentialTransition;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Only extends Application {
class SimpleSlideShow {
StackPane root = new StackPane();
ImageView[] slides;
public SimpleSlideShow() {
this.slides = new ImageView[4];
Image image1 = new Image(Only.class.getResource("res/Pic1.jpg").toExternalForm());
Image image2 = new Image(Only.class.getResource("res/Pic2.jpeg").toExternalForm());
Image image3 = new Image(Only.class.getResource("res/Pic3.jpeg").toExternalForm());
Image image4 = new Image(Only.class.getResource("res/Pic4.jpeg").toExternalForm());
slides[0] = new ImageView(image1);
slides[1] = new ImageView(image2);
slides[2] = new ImageView(image3);
slides[3] = new ImageView(image4);
}
public StackPane getRoot() {
return root;
}
public ImageView[] getImageView() {
return slides;
}
/*public void start() {
this.root.getChildren().add(slides[0]);
// slide.setOpacity(0);
Timeline timeline = new Timeline();
// TranslateTransition tts= new TranslateTransition(Duration.millis(3000));
slides[0].translateXProperty().set(-1*root.getWidth());
KeyValue kv = new KeyValue(slides[0].translateXProperty(), 0, Interpolator.EASE_IN);
KeyFrame kf = new KeyFrame(Duration.seconds(2), kv);
timeline.getKeyFrames().add(kf);
timeline.setAutoReverse(true);
timeline.setOnFinished(t -> {
this.root.getChildren().addAll(slides[1],slides[2],slides[3]);
});
timeline.play();
}*/
public void leftToRight(){
this.root.getChildren().add(slides[0]);
// slide.setOpacity(0);
Timeline timeline = new Timeline();
// TranslateTransition tts= new TranslateTransition(Duration.millis(3000));
KeyValue kv = new KeyValue(slides[0].translateXProperty(), 0, Interpolator.EASE_IN);
slides[0].translateXProperty().set(-1*root.getWidth());
KeyFrame kf = new KeyFrame(Duration.seconds(2), kv);
timeline.getKeyFrames().add(kf);
timeline.setAutoReverse(true);
timeline.setOnFinished(t -> {
this.root.getChildren().addAll(slides[1]);
});
timeline.play();
/*SequentialTransition st = new SequentialTransition();
for (ImageView sl : slides) {
this.root.getChildren().add(slides[0]);
slides[0].translateXProperty().add(root.getScene().getHeight());*/
}
}
}
/*public FadeTransition getFadeTransition(ImageView imageView, double fromValue, double toValue,
int durationInMilliseconds) {
// SequentialTransition slide = new SequentialTransition();
// slide.setCycleCount(2);
// slide.autoReverseProperty();
// slide.setAutoReverse(true);
FadeTransition ft = new FadeTransition(Duration.seconds(3), imageView);
ft.setFromValue(fromValue);
ft.setToValue(toValue);
return ft;
}
// }
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
SimpleSlideShow simpleSlideShow = new SimpleSlideShow();
Scene scene = new Scene(simpleSlideShow.getRoot());
primaryStage.setScene(scene);
primaryStage.setMaximized(true);
primaryStage.show();
simpleSlideShow.getImageView()[0].translateXProperty().set(scene.getWidth());
//simpleSlideShow.start();
simpleSlideShow.leftToRight();
}
}
I'm new to JavaFX and I'm trying to make a menu that can be any size.
I've tried every layout possible for hours but I can't get a simple design done.
My background is a black Rectangle. I want the title to be centered on top of the screen, and my menu to be centered below the title.
Plus I want the stage size to be fixed to the Rectangle size, so that we don't see white on the background.
Here's my mvce :
package mvce_poneymon_menu;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Mvce_poneymon_menu extends Application {
#Override
public void start(Stage stage) throws Exception {
MenuView menuView = new MenuView(600, 600);
Group root = new Group();
Scene scene = new Scene(root);
stage.setTitle("Poneymon");
stage.setScene(scene);
root.getChildren().add(menuView);
menuView.requestFocus();
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MenuView.java :
package mvce_poneymon_menu;
import javafx.animation.TranslateTransition;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.util.Duration;
public class MenuView extends StackPane {
static final Font FONT = Font.font("", FontWeight.BOLD, 50);
int width;
int height;
VBox menuBox;
int currentItem = 0;
public MenuView(int w, int h) {
width = w;
height = h;
createContent();
setOnKeyPressedEvent();
}
private void createContent() {
GridPane grid = new GridPane();
MenuItem exitItem = new MenuItem("Exit");
exitItem.setOnActivate(() -> System.exit(0));
menuBox = new VBox(10,
new MenuItem("Start a game"),
new MenuItem("Parameters"),
exitItem);
menuBox.setAlignment(Pos.CENTER);
menuBox.setTranslateX(360);
getMenuItem(0).setActive(true);
HBox title = (HBox)createTitle("Poneymon");
grid.add(title, 0, 0);
grid.add(menuBox, 0, 1);
Rectangle bg = new Rectangle(width, height);
grid.setTranslateY(25);
this.getChildren().addAll(bg, grid);
}
private Node createTitle(String title) {
HBox letters = new HBox(0);
letters.setAlignment(Pos.CENTER);
for (int i = 0; i < title.length(); i++) {
Text letter = new Text(title.charAt(i) + "");
letter.setFont(FONT);
letter.setFill(Color.WHITE);
letters.getChildren().add(letter);
TranslateTransition tt = new TranslateTransition(Duration.seconds(2), letter);
tt.setDelay(Duration.millis(i * 50));
tt.setToY(-25);
tt.setAutoReverse(true);
tt.setCycleCount(TranslateTransition.INDEFINITE);
tt.play();
}
return letters;
}
private MenuItem getMenuItem(int index) {
return (MenuItem)menuBox.getChildren().get(index);
}
private void setOnKeyPressedEvent() {
this.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.UP) {
if (currentItem > 0) {
getMenuItem(currentItem).setActive(false);
getMenuItem(--currentItem).setActive(true);
}
}
if (e.getCode() == KeyCode.DOWN) {
if (currentItem < menuBox.getChildren().size() - 1) {
getMenuItem(currentItem).setActive(false);
getMenuItem(++currentItem).setActive(true);
}
}
if (e.getCode() == KeyCode.ENTER) {
getMenuItem(currentItem).activate();
}
}
});
}
}
MenuItem.java :
package mvce_poneymon_menu;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
public class MenuItem extends HBox {
static final Font FONT = Font.font("", FontWeight.BOLD, 30);
private TriCircle c1 = new TriCircle();
private TriCircle c2 = new TriCircle();
private Text text;
private Runnable script;
private static class TriCircle extends Parent {
public TriCircle() {
Shape shape1 = Shape.subtract(new Circle(5), new Circle(2));
shape1.setFill(Color.WHITE);
Shape shape2 = Shape.subtract(new Circle(5), new Circle(2));
shape2.setFill(Color.WHITE);
shape2. setTranslateX(5);
Shape shape3 = Shape.subtract(new Circle(5), new Circle(2));
shape3.setFill(Color.WHITE);
shape3.setTranslateX(2.5);
shape3.setTranslateY(-5);
getChildren().addAll(shape1, shape2, shape3);
setEffect(new GaussianBlur(2));
}
}
public MenuItem(String name) {
super(15);
setAlignment(Pos.CENTER);
text = new Text(name);
text.setFont(FONT);
text.setEffect(new GaussianBlur(2));
getChildren().addAll(c1, text, c2);
setActive(false);
setOnActivate(() -> System.out.println(name + " activated"));
}
public void setActive(boolean b) {
c1.setVisible(b);
c2.setVisible(b);
text.setFill(b ? Color.WHITE : Color.GREY);
}
public void setOnActivate(Runnable r) {
script = r;
}
public void activate() {
if (script != null) {
script.run();
}
}
}
I'm sure this is very simple but I can't figure it out :c
My background is a black Rectangle. [...] Plus I want the stage size to be fixed to the Rectangle size, so that we don't see white on the background.
It would be much simpler to simply assign a background to the StackPane. This would allow you to resize MenuView and keep the size of the background the same as the size of the MenuView without additional logic.
Preventing resizing of the window should be done for the Stage using setResizable.
I want the title to be centered on top of the screen, and my menu to be centered below the title.
You're using an "unhealthy" amount of transformation properties. (I'm refering to translateX and translateY in this case.) These properties are not taken into account by the parent layout; during the layout the nodes are positioned where the same node without any transformation would be positioned and the rendering algorithm considers those transformations though.
Imho the following structure would suit the desired outcome better:
MenuView (root)
|- VBox (place menu items below title)
|- HBox (title container)
|- ...
|- MenuItem
|- MenuItem
|- MenuItem
To get the correct size for the title container, I recommend using a padding around the content.
There are several other things that I'd change:
Shape shape1 = Shape.subtract(new Circle(5), new Circle(2));
shape1.setFill(Color.WHITE);
I'd recommend changing this to circles with a stroke instead oc intersecting shapes.
Instead of blurring every child in MenuItem seperatly I'd recommend applying a blur on the item itself.
The TriCircle class does not contain any logic other than setting up the nodes. It could (and should) be replaced by a method creating a Group containing the circles.
#Override
public void start(Stage stage) {
MenuView menuView = new MenuView(600, 600);
Scene scene = new Scene(menuView);
stage.setTitle("Poneymon");
stage.setScene(scene);
menuView.requestFocus();
stage.setResizable(false); // prevent resizing of stage
stage.show();
}
public class MenuView extends StackPane {
static final Font FONT = Font.font("", FontWeight.BOLD, 50);
int currentItem = 0;
public MenuView(int w, int h) {
setPrefSize(w, h);
createContent();
setOnKeyPressedEvent();
}
private List<MenuItem> menuItems;
private void createContent() {
MenuItem exitItem = new MenuItem("Exit");
exitItem.setOnActivate(() -> Platform.exit());
menuItems = Arrays.asList(
new MenuItem("Start a game"),
new MenuItem("Parameters"),
exitItem);
VBox container = new VBox(10, createTitle("Poneymon"));
container.getChildren().addAll(menuItems);
container.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
getMenuItem(0).setActive(true);
setBackground(new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY)));
getChildren().add(container);
}
private Node createTitle(String title) {
final double movement = 25;
HBox letters = new HBox();
letters.setAlignment(Pos.CENTER);
// add space on top equla to the upwards movement of the letters
letters.setPadding(new Insets(movement, 0, 0, 0));
for (int i = 0; i < title.length(); i++) {
Text letter = new Text(title.charAt(i) + "");
letter.setFont(FONT);
letter.setFill(Color.WHITE);
letters.getChildren().add(letter);
TranslateTransition tt = new TranslateTransition(Duration.seconds(2), letter);
tt.setDelay(Duration.millis(i * 50));
tt.setToY(-movement);
tt.setAutoReverse(true);
tt.setCycleCount(TranslateTransition.INDEFINITE);
tt.play();
}
return letters;
}
private MenuItem getMenuItem(int index) {
return menuItems.get(index);
}
private void setOnKeyPressedEvent() {
this.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.UP) {
if (currentItem > 0) {
getMenuItem(currentItem).setActive(false);
getMenuItem(--currentItem).setActive(true);
}
}
if (e.getCode() == KeyCode.DOWN) {
if (currentItem < menuItems.size() - 1) {
getMenuItem(currentItem).setActive(false);
getMenuItem(++currentItem).setActive(true);
}
}
if (e.getCode() == KeyCode.ENTER) {
getMenuItem(currentItem).activate();
}
}
});
}
}
public class MenuItem extends HBox {
static final Font FONT = Font.font("", FontWeight.BOLD, 30);
private Group c1 = createTriCircle();
private Group c2 = createTriCircle();
private Text text;
private Runnable script;
private static Circle createCircle(double centerX, double centerY) {
final double innerRadius = 2;
final double outerRadius = 5;
Circle circle = new Circle(centerX, centerY, (innerRadius + outerRadius) / 2, null);
circle.setStroke(Color.WHITE);
circle.setStrokeWidth(outerRadius - innerRadius);
return circle;
}
private static Group createTriCircle() {
return new Group(
createCircle(0, 0),
createCircle(5, 0),
createCircle(2.5, -5));
}
public MenuItem(String name) {
super(15);
setAlignment(Pos.CENTER);
text = new Text(name);
text.setFont(FONT);
setEffect(new GaussianBlur(2));
getChildren().addAll(c1, text, c2);
setActive(false);
setOnActivate(() -> System.out.println(name + " activated"));
}
public void setActive(boolean b) {
c1.setVisible(b);
c2.setVisible(b);
text.setFill(b ? Color.WHITE : Color.GREY);
}
public void setOnActivate(Runnable r) {
script = r;
}
public void activate() {
if (script != null) {
script.run();
}
}
}
To adjust the distance between title and menu items, you could use VBox.setMargin.
package javafx;
import java.util.ArrayList;
import java.util.List;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ImageSlide extends Application {
// Width and height of image in pixels
private final double IMG_WIDTH = 600;
private final double IMG_HEIGHT = 300;
private final int NUM_OF_IMGS = 3;
private final int SLIDE_FREQ = 4; // in secs
#Override
public void start(Stage stage) throws Exception {
//root code
StackPane root = new StackPane();
Pane clipPane = new Pane();
// To center the slide show incase maximized
clipPane.setMaxSize(IMG_WIDTH, IMG_HEIGHT);
clipPane.setClip(new Rectangle(IMG_WIDTH, IMG_HEIGHT));
HBox imgContainer = new HBox();
//image view
ImageView imgGreen = new ImageView(new Image(getClass()
.getResourceAsStream("\\Merged1.pngg")));
ImageView imgBlue = new ImageView(new Image(getClass()
.getResourceAsStream("\\Merged2.png")));
ImageView imgRose = new ImageView(new Image(getClass()
.getResourceAsStream("\\Merged3.png")));
imgContainer.getChildren().addAll(imgGreen, imgBlue, imgRose);
clipPane.getChildren().add(imgContainer);
root.getChildren().add(clipPane);
Scene scene = new Scene(root, IMG_WIDTH, IMG_HEIGHT);
stage.setTitle("Image Slider");
stage.setScene(scene);
startAnimation(imgContainer);
stage.show();
}
//start animation
private void startAnimation(final HBox hbox) {
//error occured on (ActionEvent t) line
//slide action
EventHandler<ActionEvent> slideAction = (ActionEvent t) {
TranslateTransition trans = new TranslateTransition(Duration.seconds(1.5), hbox);
trans.setByX(-IMG_WIDTH);
trans.setInterpolator(Interpolator.EASE_BOTH);
trans.play();
};
//eventHandler
EventHandler<ActionEvent> resetAction = (ActionEvent t) {
TranslateTransition trans = new TranslateTransition(Duration.seconds(1), hbox);
trans.setByX((NUM_OF_IMGS - 1) * IMG_WIDTH);
trans.setInterpolator(Interpolator.EASE_BOTH);
trans.play();
};
List<KeyFrame> keyFrames = new ArrayList<>();
for (int i = 1; i <= NUM_OF_IMGS; i++) {
if (i == NUM_OF_IMGS) {
keyFrames.add(new KeyFrame(Duration.seconds(i * SLIDE_FREQ), resetAction));
} else {
keyFrames.add(new KeyFrame(Duration.seconds(i * SLIDE_FREQ), slideAction));
}
}
//add timeLine
Timeline anim = new Timeline(keyFrames.toArray(new KeyFrame[NUM_OF_IMGS]));
anim.setCycleCount(Timeline.INDEFINITE);
anim.playFromStart();
}
//call main function
public static void main(String[] args) {
launch(args);
}
}
The lines
EventHandler<ActionEvent> slideAction = (ActionEvent t) {
TranslateTransition trans = new TranslateTransition(Duration.seconds(1.5), hbox);
trans.setByX(-IMG_WIDTH);
trans.setInterpolator(Interpolator.EASE_BOTH);
trans.play();
};
should be
EventHandler<ActionEvent> slideAction = (ActionEvent t) -> {
TranslateTransition trans = new TranslateTransition(Duration.seconds(1.5), hbox);
trans.setByX(-IMG_WIDTH);
trans.setInterpolator(Interpolator.EASE_BOTH);
trans.play();
};
Note the only addition ->. Which IDE are you using?