As it is my first application in javafx and trying to make a simple calculator. I want to know how I can make this for loop in easy way :
for(int i=1; i<10; i++){
Button bt1 = new Button();
bt1.setText("1");
bt1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("1");
}
});
i 1;
}
And how to get this buttons in VBox.
I don't know why it takes a lot of time to do that
but as jewels said it should be very quick in doing that, i'm working with javafx since 9 months at work on a computer that is not very performant and, as i understand it, in the calculator you would have 10 buttons for numbers from 0 to 9, i'd code it like this:
VBox vboxForButtons = new VBox();
for(int i=0; i<10; i++){
Button btnNumber = new Button();
btnNumber.setText(String.valueOf(i));
btnNumber.setOnAction((ActionEvent)->{
System.out.println(btnNumber.getText());
});
vboxForButtons.getChildren().add(btnNumber);
}
In alternative to this, for small and slight applications i advice you to use the SceneBuilder tool linking directly the objects that you drag in the window to an FXML event in the code.
I hope this answer will help you.
You can achieve it using the following code.
VBox vbox = new VBox(); //create new VBox instance
vbox.setMinWidth(500); //set minimum width, dont think you'd need this.
for(int i=1; i<10; i++){
Button btn = new Button();
btn.setMinWidth(200);
btn.setMinHeight(50);
btn.setText("Button" + i);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println(btn.getText()); //Or "1" as in your code
}
});
vbox.getChildren().add(btn); //add button to your VBox
}
root.getChildren().add(vbox); //root of your pane
primaryStage.setScene(scene); //primaryStage is your stage, scene is the current scene
primaryStage.show();
Edit
Reading the comments in your question about performance issue, this will not fix any of it. I have basically written the same code that you've used above, and added VBox creation and adding Button to it, because from the question it sounded like you needed help with creating VBox and adding Buttons to it.
Related
I am building a game engine as a school project. When I add button to the same group as where I have my canvas, I can't control my player anymore with keyboard. Buttons and everything else still works like normal.
My code is pretty huge, so this is a simplified code of the problem:
public abstract class GEngine extends Application {
public void start(Stage theStage) throws Exception {
try {
this.stage = theStage;
Group root = new Group();
Scene theScene = new Scene(root);
stage.setScene(theScene);
Canvas canvas = new Canvas(windowWidth, windowHeight);
root.getChildren().add(canvas);
Button btn = new Button("new");
btn.setOnMousePressed(e->System.out.println("press"));
root.getChildren().add(btn);
Timeline gameLoop = new Timeline();
gameLoop.setCycleCount(Timeline.INDEFINITE);
// Handle input
theScene.setOnKeyPressed(Input::handlePressed);
theScene.setOnKeyReleased(Input::handleReleased);
// Control game loop and it's speed
KeyFrame kf = new KeyFrame(
Duration.seconds(0.017), // 60 FPS
(e)->this.controlGameLoop());
gameLoop.getKeyFrames().add( kf );
gameLoop.play();
stage.show();
} catch (Exception e) {
e.printStackTrace();
stop();
}
}
}
There is probably something happening on the background which I just don't understand. I can show my Input class code too if somebody wants to see it, but in my understanding it's not necessary.
I have tried using AnchorPane as main root and make a separate groups for buttons and canvas which I add the to the AnchorPane, but that did not help at all. That was pretty much the only offered solution I could find from google.
Adding btn.setFocusTraversable(false); fixed the problem, thanks to Luxusproblem for providing the answer!
I'm trying to code a 2048 game using JavaFX and I'm facing a problem.
#Override
public void start(Stage primaryStage){
primaryStage.setResizable(false);
Scene scene = new Scene(firstContent());
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent e){
KeyCode key = e.getCode();
if((key.equals(KeyCode.UP))){
System.out.println("recieved UP");
Scene scene = new Scene(createContent());
primaryStage.setScene(scene);
primaryStage.show();
} else if(key.equals(KeyCode.DOWN)){
System.out.println("recieved DOWN");
}
}
});
}
So here I open my window initialised with firstContent (basically it creates an array of empty tiles, and fills two of them with 2 or 4 randomly), display it and start listening for key presses. The idea is to have a behavior for each arrow key (UP DOWN LEFT RIGHT) which will move the tiles accordingly. This is done by the following createContent() method :
public Parent createContent(){
String c = "";
List<Integer> known = new ArrayList<Integer>();
Pane root = new Pane();
root.setPrefSize(740, 700);
Random rand = new Random();
int pos1 = rand.nextInt(15);
if(tiles.get(pos1) != new Tile("")){
known.add(pos1);
pos1 = rand.nextInt(15);
if(known.contains(pos1)){
known.add(pos1);
pos1 = rand.nextInt(15);
}
}
for(int i = 0; i < NB_TILES; i++){
tiles.add(new Tile(c));
}
tiles.set(pos1, new Tile("2048"));
for(int i = 0; i < tiles.size(); i++){
// boring stuff to set the tile display to the right size
}
return root;
}
Now for the problem : when the application is running, if I press the down arrow, I do get on my terminal the "recieved DOWN" text as many times as I press the key as expected. But if I press the up arrow, the application will only recieve it once and the application seems to be frozen (meaning if I press down again, nothing happens).
As you could guess, I want to be able to call my method for each key press to be able to move my tiles around and utltimately combine them to get a playable version of 2048... Anyone know why my app gets frozen ?
If needed, I can provide other bits of code but I think I provided the essential. Just know that firstContent() works basically the same as createContent for now except it genereates two random numbers to get the first tiles of the game.
Thanks in advance for your help.
I'm trying to make a program that will display a random set of 4 cards, then when I click the button again it will clear the old set and display a new random set.
Right now my program will display 4 random images of cards when I click the button; however, when I try to click it again nothing happens. I'm assuming it has something to do with the EventHandler no longer being registered to the button after I clear the root children. However, I don't know how to go about fixing this. Any help is greatly appreciated! I haven't been able to find an answer to this yet, and have only been learning JavaFX for about a week. Thank you.
The code I have so far:
public class CardShuffle extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
Scanner input = new Scanner(System.in);
File cardsFolder = new File("C:\\Users\\timsp\\Pictures\\JPEG");
ArrayList<File> cardsFilePaths = new ArrayList<File> (Arrays.asList(cardsFolder.listFiles()));
Button deal = new Button("DEAL");
Pane hb = new HBox(10);
hb.setPadding(new Insets(5, 5, 5, 5));
root.getChildren().add(deal);
deal.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().clear();
ArrayList<ImageView> cards = getRandomCards(cardsFilePaths);
for (int i = 0; i < 4; i++) {
cards.get(i).setFitWidth(150);
cards.get(i).setFitHeight(100);
hb.getChildren().add(cards.get(i));
}
root.getChildren().addAll(deal, hb);
}
});
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public ArrayList<ImageView> getRandomCards(ArrayList<File> cardsFilePaths) {
ArrayList<ImageView> cards = new ArrayList<ImageView>();
try {
for (int i = 0; i < 4; i++) {
Image card = new Image((new FileInputStream(cardsFilePaths.get((int) (Math.random() * 52)).getPath())));
ImageView temp = new ImageView();
temp.setImage(card);
cards.add(temp);
}
} catch (FileNotFoundException e) {
e.getMessage();
}
return cards;
}
}
Many problems here :
the first one, and the most important (because it hides your further error) is the root layout : you use a StackPane, the first thing you should do is to replace it by a VBox for example and rerun your program, it will be easier to see what really happens. (you will not have 4 cards, but 8, 12, 16 and so on).
the first one generates the second one. By doing this root.getChildren().addAll(deal, hb); you put the HBox layout above the button, and the click is first consumed by the HBox. Here is an example to see it more easily :
// Add the HBox as soon as the initialization
root.getChildren().add(deal);
hb.setOnMouseClicked(e -> System.out.println("HBox clicked"));
deal.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().clear();
ArrayList<ImageView> cards = getRandomCards(cardsFilePaths);
for(int i = 0; i < 4; i++) {
cards.get(i).setFitWidth(150);
cards.get(i).setFitHeight(100);
hb.getChildren().add(cards.get(i));
}
hb.setStyle("-fx-background-color:CORNFLOWERBLUE;-fx-opacity:0.8;");
root.getChildren().addAll(deal, hb);
}
});
And the last one, you don't really want to remove all root's children, what you want is to replace your cards by another 4 ones. Thus it is not necessary to remove the button, only the HBox can be manipulated as shown by the following example :
// Add the HBox as soon as the initialization
root.getChildren().addAll(hb, deal);
deal.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// root.getChildren().clear();
// Replace the previous line by the following :
hb.getChildren().clear();
ArrayList<ImageView> cards = getRandomCards(cardsFilePaths);
for(int i = 0; i < 4; i++) {
cards.get(i).setFitWidth(150);
cards.get(i).setFitHeight(100);
hb.getChildren().add(cards.get(i));
}
// The following is useless now.
// root.getChildren().addAll(hb, deal);
}
});
A few days ago I started studying JavaFX, and came across the desire to perform 2 experiments. Firstly, I would like to know if it is possible to put an animated background behind an user interface. I've succeeded in creating an animated background, and now I'm having great difficulties to position some controls in the middle of my interface.
I'd like to introduce you 2 pictures of my program. The first demonstrates the undesirable result that I'm getting:
I believe this is my nodes tree:
This is the code of my application:
public class AnimatedBackground extends Application
{
// #########################################################################################################
// MAIN
// #########################################################################################################
public static void main(String[] args)
{
Application.launch(args);
}
// #########################################################################################################
// INSTÂNCIAS
// #########################################################################################################
private Group root;
private Group grp_hexagons;
private Rectangle rect_background;
private Scene cenario;
// UI
private VBox lay_box_controls;
private Label lab_test;
private TextArea texA_test;
private Button bot_test;
// #########################################################################################################
// INÍCIO FX
// #########################################################################################################
#Override public void start(Stage stage) throws Exception
{
this.confFX();
cenario = new Scene(this.root , 640 , 480);
this.rect_background.widthProperty().bind(this.cenario.widthProperty());
this.rect_background.heightProperty().bind(this.cenario.heightProperty());
stage.setScene(cenario);
stage.setTitle("Meu programa JavaFX - R.D.S.");
stage.show();
}
protected void confFX()
{
this.root = new Group();
this.grp_hexagons = new Group();
// Initiate the circles and all animation stuff.
for(int cont = 0 ; cont < 15 ; cont++)
{
Circle circle = new Circle();
circle.setFill(Color.WHITE);
circle.setEffect(new GaussianBlur(Math.random() * 8 + 2));
circle.setOpacity(Math.random());
circle.setRadius(20);
this.grp_hexagons.getChildren().add(circle);
double randScale = (Math.random() * 4) + 1;
KeyValue kValueX = new KeyValue(circle.scaleXProperty() , randScale);
KeyValue kValueY = new KeyValue(circle.scaleYProperty() , randScale);
KeyFrame kFrame = new KeyFrame(Duration.millis(5000 + (Math.random() * 5000)) , kValueX , kValueY);
Timeline linhaT = new Timeline();
linhaT.getKeyFrames().add(kFrame);
linhaT.setAutoReverse(true);
linhaT.setCycleCount(Animation.INDEFINITE);
linhaT.play();
}
this.rect_background = new Rectangle();
this.root.getChildren().add(this.rect_background);
this.root.getChildren().add(this.grp_hexagons);
// UI
this.lay_box_controls = new VBox();
this.lay_box_controls.setSpacing(20);
this.lay_box_controls.setAlignment(Pos.CENTER);
this.bot_test = new Button("CHANGE POSITIONS");
this.bot_test.setAlignment(Pos.CENTER);
this.bot_test.setOnAction(new EventHandler<ActionEvent>()
{
#Override public void handle(ActionEvent e)
{
for(Node hexagono : grp_hexagons.getChildren())
{
hexagono.setTranslateX(Math.random() * cenario.getWidth());
hexagono.setTranslateY(Math.random() * cenario.getHeight());
}
}
});
this.texA_test = new TextArea();
this.texA_test.setText("This is just a test.");
this.lab_test = new Label("This is just a label.");
this.lab_test.setTextFill(Color.WHITE);
this.lab_test.setFont(new Font(32));
this.lay_box_controls.getChildren().add(this.lab_test);
this.lay_box_controls.getChildren().add(this.texA_test);
this.lay_box_controls.getChildren().add(this.bot_test);
this.root.getChildren().add(this.lay_box_controls);
}
}
I've tried to make the use of a StackPane as the root of my scene graph, but also found an undesired result. Despite the controls have stayed in the center of the window, the circles begin to move in as they grow and shrink, making it appear that everything is weird.
The second thing I would like to know is if it is possible to customize the controls so they perform some animation when some event happens. Although we can change the appearance of controls using CSS, it's harder to create something complex. For example, when a control changes its appearance due to a change of state, the transition state change is not made in an animated way, but in an abrupt and static way. Is there a way to animate, for example, a button between its states? This would be done using the JavaFX API? Or would that be using CSS? Or would not be possible in any way?
Thank you for your attention.
after much struggle, I and some users of the Oracle community could resolve this issue. I see no need to repeat here all the resolution made by us, so I'll post the link so you can access the solution of the problem. I hope this benefits us all. Thanks for your attention anyway.
https://community.oracle.com/thread/2620500
I have a toolbar with several buttons. I want several of this buttons to load different FXML-files. The way it is done now is by rwiting the fxml file in Java code and inserting it everytime it get's called. It would be a much better choice if I could just call the FXML-file and get the correct scene. This is an example where i load a settings view:
public void showModelSettings(){
clearPane();
GridPane gridPane = new GridPane();
ColumnConstraints cc1 = new ColumnConstraints();
cc1.setPercentWidth(50);
ColumnConstraints cc2 = new ColumnConstraints();
cc2.setPercentWidth(50);
gridPane.getColumnConstraints().addAll(cc1,cc2);
RowConstraints rc1 = new RowConstraints();
rc1.setPercentHeight(30);
RowConstraints rc2 = new RowConstraints();
rc2.setPercentHeight(70);
gridPane.getRowConstraints().addAll(rc1,rc2);
//iwModel.setImage(new Image("C:\Users\Public\Pictures\Sample Pictures\Desert.jpg"));
gridPane.setConstraints(cbModel, 0, 0);
gridPane.setConstraints(btnImageChooser, 1, 0);
gridPane.setConstraints(iwModel, 0, 1, 2, 1);
gridPane.getChildren().addAll(cbModel, btnImageChooser,iwModel);
mainPanel.getChildren().add(gridPane);
}
I have used tabpane at an earlier date, the handy thing with this is that it can load "content" which in a way is a actionlistener that can load fxml-files. I want this functionality for my toolbar buttons.
all help will be appreciated greatly!
Thanks :)
I think maybe I solved it, can't check yet because my project has several errors in other packages. But here is the code I believe will work:
private Parent replaceSceneContent(String fxml) throws Exception {
Parent page = (Parent) FXMLLoader.load(InventorySystem.class.getResource("/hist/inventory/gui/fxml/"+fxml), ResourceBundle.getBundle("MessageBundle",localSettings.getLocale()), new JavaFXBuilderFactory());
Scene scene = stage.getScene();
if(scene == null) {
scene = new Scene(page, 340, 280);
scene.getStylesheets().add("hist/inventory/gui/startStyle.css"); //Endre styles?
//stage.setResizable(false); //Må kanskje flyttes
stage.setScene(scene);
Organizer.getWindowFitter().setScene(scene);
} else {
stage.getScene().setRoot(page);
}
stage.sizeToScene();
return page;
}
public void setScene(String scene) throws Exception {
replaceSceneContent(scene);
}