Dynamically updating a GridPane with new Buttons in JavaFX -> how to repaint? - java

I have a grid pane which contains a number of buttons (normally something between 10 and 25) with five buttons per row (and however many are left in the last row). The number of buttons (and the buttons itself) might change during program execution. When that happens, the new buttons should be displayed. How can I achieve that? Here is a mini-example:
public class GridButtons extends Application {
List<String> buttonTexts = new ArrayList<>();
GridPane buttonGrid = new GridPane();
GridPane bgGrid = new GridPane();
#Override
public void start(Stage primaryStage) {
Button changeButtonsButton = new Button("Change BTNs");
changeButtonsButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
changeButtonTexts();
}
});
bgGrid.add(changeButtonsButton, 0, 0);
changeButtonTexts();
bgGrid.add(buttonGrid, 1, 0);
Scene scene = new Scene(bgGrid, 440, 140);
primaryStage.setScene(scene);
primaryStage.show();
}
public void updateButtonsGrid() {
buttonGrid = new GridPane();
for (int i = 0; i < buttonTexts.size(); i++) {
Button button = new Button(buttonTexts.get(i));
button.setMinWidth(70);
button.setMaxWidth(70);
buttonGrid.add(button, i % 5, i / 5);
System.out.println(buttonTexts.get(i));
}
// now the new GridPane should be displayed -> how?
}
public void changeButtonTexts() {
buttonTexts.clear();
Random random = new Random();
int buttonCount = random.nextInt(15) + 10;
for (int i = 0; i < buttonCount; i++) {
buttonTexts.add("Button " + i);
}
updateButtonsGrid();
}
public static void main(String[] args) {
launch(args);
}
}
Or are there better options than using a GridPane? Using a ListView<Button> and an ObservableList<Button> would work, but then the buttons are not displayed in a tabular form with five buttons in each row.

Creating a new GridPane is probably not what you want here. In the function updateButtonsGrid, change the line
buttonGrid = new GridPane();
to
buttonGrid.getChildren().clear();
If, for some reason, you are absolutely sure you need a new instance of GridPane, remove the old one from bgGrid, and add the new one. In your current code example the new instance of GridPane is never added to the scene graph.

Related

How to drag and drop button onto GridPane?

I'm working on a school project creating a simple battleship game and i want to use the Drag And Drop function to drag buttons from HBox on the bottom of the screen to the GridPane that the player uses to place ships on. But i can't get it to work properly. Here's a picture of my board per now.
I have tried to use the button.setOnMouseDragged(e -> {CODE HERE}); but it doesnt work.
Here is the code i used for my window
public class GridOrganizer {
private BorderPane borderPane;
public GridOrganizer() {
borderPane = new BorderPane();
borderPane.setStyle("-fx-background-color: grey;");
borderPane.setPrefHeight(600);
borderPane.setPrefWidth(600);
createGrid();
}
public void createGrid() {
//Creates the grids where the game is played and buttons/ships to place on grid
GridPane playerGrid = new GridPane();
GridPane enemyGrid = new GridPane();
Insets padding = new Insets(10);
//Create playergrid
for (int i = 0; i < 10; i++) {
playerGrid.getColumnConstraints().add(new ColumnConstraints(50)); //50 wide
playerGrid.getRowConstraints().add(new RowConstraints(50));
}
//Create enemygrid
for (int i = 0; i < 10; i++) {
enemyGrid.getColumnConstraints().add(new ColumnConstraints(50)); //50 wide
enemyGrid.getRowConstraints().add(new RowConstraints(50));
}
//looping through row and columns and adds buttons
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
Button button = new Button();
button.setPrefHeight(50);
button.setPrefWidth(50);
GridPane.setConstraints(button, j, i); //(button, column, row)
playerGrid.getChildren().add(button); //add button on each index
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//HIT or MISS
System.out.println("Row: " + GridPane.getRowIndex(button) + ", Column: " + GridPane.getColumnIndex(button));
button.setStyle("-fx-background-color: grey;");
}
});
}
}
//..same with enemy grid
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
Button button = new Button();
button.setPrefHeight(50);
button.setPrefWidth(50);
GridPane.setConstraints(button, j, i); //(button, column, row)
enemyGrid.getChildren().add(button); //add button on each index
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
System.out.println("Row: " + GridPane.getRowIndex(button) + ", Column: " + GridPane.getColumnIndex(button));
button.setStyle("-fx-background-color: grey;");
}
});
}
}
//Make buttons for the ships
Button rowboat = new Button("Rowboat");
Button sailboat = new Button("Sailboat");
Button submarine = new Button("Submarine");
Button destroyer = new Button("Destroyer");
Button battleship = new Button("Battleship");
//Size the ship buttons to match game description
battleship.setPrefHeight(50);
battleship.setPrefWidth(250); //size 5
destroyer.setPrefHeight(50);
destroyer.setPrefWidth(200); //size 4
submarine.setPrefHeight(50);
submarine.setPrefWidth(150); //size 3
sailboat.setPrefHeight(50);
sailboat.setPrefWidth(150); //size 3
rowboat.setPrefHeight(50);
rowboat.setPrefWidth(100); //size 2
//Drags button
rowboat.setOnMouseDragged(e -> {
//CODE HERE
});
//Drops button on grid
//CODE HERE
sailboat.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
submarine.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
destroyer.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
battleship.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
//PLACE SHIP
}
});
HBox ships = new HBox(); //Horizontal box
ships.getChildren().addAll(rowboat, sailboat, submarine, destroyer, battleship); //Add buttons to horizontal box
//Add grids and ship buttons to pane with padding
borderPane.setLeft(enemyGrid);
BorderPane.setMargin(enemyGrid, padding);
borderPane.setRight(playerGrid);
BorderPane.setMargin(playerGrid, padding);
borderPane.setBottom(ships);
BorderPane.setMargin(ships, padding);
}
public Pane getGrid() {
return borderPane;
}
}
The plan here is to start a drag event with the data needed to create your button when the drag event finished. In your code, it looks like you have added the Buttons into the Grid already. That means you need to only transfer a String to change the Button's text. In my code, I am using a StackPane when creating the Grid. I then create and add the Buttons later. You approach may be better. I have looked that far. I have added an MCVE (Altered code from here):
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Demonstrates a drag-and-drop feature.
*/
public class HelloDragAndDrop extends Application
{
#Override
public void start(Stage stage)
{
//Source Buttons.
final Button boat1 = new Button("boat1");
final Button boat2 = new Button("boat2");
final Button boat3 = new Button("boat3");
final Button boat4 = new Button("boat4");
//Adding OnDragDetected to source Buttons.
setOnDragDetected(boat1);
setOnDragDetected(boat2);
setOnDragDetected(boat3);
setOnDragDetected(boat4);
//Adding onDragDone to source Buttons.
setOnDragDone(boat1);
setOnDragDone(boat2);
setOnDragDone(boat3);
setOnDragDone(boat4);
//Creating GridPane
GridPane gridPane = new GridPane();
gridPane.setVgap(5);
gridPane.setHgap(5);
gridPane.setPadding(new Insets(5, 5, 5, 5));
gridPane.setStyle("-fx-background-color: black;");
//Adding StackPane to every Cell in the GridPane and Adding the Target Events to each StackPane.
for (int i = 0; i < 6; i++) {
StackPane stackPane = new StackPane();
stackPane.setPrefSize(150, 50);
stackPane.setStyle("-fx-background-color: yellow;");
setOnDragOver(stackPane);
setOnDragEntered(stackPane);
setOnDragExited(stackPane);
setOnDragDropped(stackPane);
gridPane.add(stackPane, i / 3, i % 3);
}
HBox root = new HBox(new VBox(boat1, boat2, boat3, boat4), gridPane);
stage.setTitle("Hello Drag And Drop");
Scene scene = new Scene(root, 400, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
//source events handlers
public void setOnDragDetected(Button source)
{
source.setOnDragDetected((MouseEvent event) -> {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
});
}
public void setOnDragDone(Button source)
{
source.setOnDragDone((DragEvent event) -> {
/* the drag-and-drop gesture ended */
System.out.println("onDragDone");
/* if the data was successfully moved, clear it */
// if (event.getTransferMode() == TransferMode.MOVE) {
// source.setText("");
// }
event.consume();
});
}
//target event handlers
public void setOnDragOver(StackPane target)
{
target.setOnDragOver((DragEvent event) -> {
/* data is dragged over the target */
System.out.println("onDragOver");
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
});
}
public void setOnDragEntered(StackPane target)
{
target.setOnDragEntered((DragEvent event) -> {
/* the drag-and-drop gesture entered the target */
System.out.println("onDragEntered");
/* show to the user that it is an actual gesture target */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
target.setStyle("-fx-background-color: green;");
}
event.consume();
});
}
public void setOnDragExited(StackPane target)
{
target.setOnDragExited((DragEvent event) -> {
/* mouse moved away, remove the graphical cues */
target.setStyle("-fx-background-color: transparent;");
event.consume();
});
}
public void setOnDragDropped(StackPane target)
{
target.setOnDragDropped((DragEvent event) -> {
/* data dropped */
System.out.println("onDragDropped");
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
//target.setText(db.getString());
Button tempBoat = new Button(db.getString());
tempBoat.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
target.getChildren().clear();
target.getChildren().add(tempBoat);
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
});
}
}
In this code, the Buttons that represent the Boats all have onDragDetected and onDragDone event handlers attached. They are considered Source/start of a Drag event. For every cell in the GridPane, a StackPane is added. These StackPanes are considered the Target/areas to drop drag events. Each StackPane has onDragOver, onDragEntered, onDragDropped, and onDragExited attached. Once a drag event is complete, the StackPane/Target that receives the event gets a new Button as a child with the same name as the Source Button.

Adding text to a label from another class. Java

In my program, there are two Form and Checked classes.
In the Form class, there is a Label and a Button. At the click in the Button I create an instance of the class Checked and start its thread.
Now, what I'm having trouble with is that I need to pass the text from the Checked class and change the Label value, but I have not succeeded.
Here is my code:
public class MainForm extends Application {
protected static int intVerifiedNews = 0;
Button btnPlay = new Button("Button");
Label lbVerifiedNews = new Label("News: ");
#Override
public void start(Stage primaryStage) throws IOException {
final BorderPane border = new BorderPane();
final HBox hbox = addHBox();
Scene scene = new Scene(border, 850, 500, Color.BLACK);
btnPlay.setPrefSize(100, 24);
btnPlay.setMinSize(24, 24);
btnPlay.setOnAction((event) -> {
Checked ch = new Checked();
ch.start();
}
);
border.setTop(hbox);
hbox.getChildren().addAll(btnPlay, lbVerifiedNews);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox addHBox() {
HBox hbox = new HBox();
hbox.setPadding(new Insets(5, 0, 5, 5));
return hbox;
}
public static void main(String[] args) throws IOException {
launch(args);
}
}
Checked class:
public class Checked extends Thread {
public void run() {
for (int i = 0; i <= 5; i++) {
MainForm.intVerifiedNews ++;
//Here you need to pass the intVerifiedNews value to the Label
System.out.println(MainForm.intVerifiedNews);
}
}
}
Pass lbVerifiedNews into the constructor of the Checked class and store this reference in a field.
Checked ch = new Checked(lbVerifiedNews);
public class Checked extends Thread {
Label checkedLabelReference;
public Checked(Label label){
this.checkedLabelReference = label;
}
public void run() {
for (int i = 0; i <= 5; i++) {
MainForm.intVerifiedNews ++;
//Here you need to pass the intVerifiedNews value to the Label
Platform.runLater(new Runnable() {
#Override public void run() {
checkedLabelReference.setText(MainForm.intVerifiedNews);//update text
}});
System.out.println(MainForm.intVerifiedNews);
}
}
}
Generically you'll want to pass reference to the MainForm or form object that you want to update into your Checked class so that you have access to its update methods directly.
public class Checked implements Runnable {
public Checked(MainForm form1) {
// store form (or the object representing the text box directly) to update later
}
public void run() {
}
}

How would I go about Streamlining this code?

I have been following an online tutorial on how to use JavaFX to create Java Gui programs.
This is one of the programs I made following using the tutorials. I decided to add a counter to the command line which would display the number of orders as well as the customers choices, although I was able to get this working the counter feature looks inefficient.
I feel adding another class to just add the orderNumber variable was wrong.
Is there any advice on streamlining this code?
Also what could I do to output the orderNumber after the Order?
public class Main extends Application {
private class Order {
public int orderNumber = 0;
}
Stage window;
Button button;
public static void main(String[] args) {
launch(args);
}
#Override
public void start (Stage primaryStage) throws Exception {
Order Number = new Order();
window = primaryStage;
window.setTitle("Luke's Sandwich's");
//Check Box's
CheckBox box1 = new CheckBox("cheese");
CheckBox box2 = new CheckBox("Bacon");
CheckBox box3 = new CheckBox("Tuna");
CheckBox box4 = new CheckBox("Tomatoes");
box1.setSelected(true);
//button
button = new Button("Order Now");
button.setOnAction(e -> {
Number.orderNumber++;
System.out.println("order: " + Number.orderNumber);
handleOptions(box1,box2,box3,box4);
});
VBox layout = new VBox(10);
layout.setPadding(new Insets(20));
layout.getChildren().addAll(box1,box2,box3,box4,button);
Scene scene = new Scene(layout,300,250);
window.setScene(scene);
window.show();
}
//Handle checkbox options
public void handleOptions (CheckBox box1,CheckBox box2,CheckBox box3,CheckBox box4){
String message = "user order:\n";
if(box1.isSelected())
message += "cheese\n";
if(box2.isSelected())
message += "bacon\n";
if(box3.isSelected())
message += "tuna\n";
if(box4.isSelected())
message += "tomatoes\n";
System.out.println(message);
}
}

java fx : Text display upon button click

I am making a basic calculator using JAVA FX and stacks. When i click any button, i get that number displayed on the screen but I want a continuous expression to be displayed upon clicking the various buttons like if i click on '5' then '+' then '3' then '-' then 1, so the screen should display "5+3-1" instead of displaying each number when i click the respective buttons.
public class Main extends Application {
/*The keyboard key values*/
private static final String[][] key_values = {
{ "7", "8", "9", "/" },
{ "4", "5", "6", "*" },
{ "1", "2", "3", "-" },
{ "0", "c", "=", "+" }
};
private Button btn[][] = new Button[4][4]; //all the keys
TextField calculator_screen; //the calculator screen
String num;
Stack <String>stack = new Stack<>();
MyStack s = new MyStack();
public static void main(String[] args)
{
launch(args);
System.out.print("123456789");
}
#Override public void start(Stage stage) {
/*The outside layout*/
final VBox layout = new VBox(30); //the size vertically
/*The inside layout for keys or buttons*/
TilePane keypad = new TilePane(); //even it is called keypad, it is a layout
keypad.setVgap(7);
keypad.setHgap(7); //set the gap between keys
/*Create Calculator Screen */
calculator_screen = new TextField();
calculator_screen.setStyle("-fx-background-color: #FFFFFF;"); //set the style of the screen
calculator_screen.setAlignment(Pos.CENTER_RIGHT); //make the screen in the center of the calculator
calculator_screen.setEditable(true); //make sure the screen cannot be typed in manually
calculator_screen.setPrefWidth(500); //set the windth of the screen
/*Create Calculator keyboard*/
keypad.setPrefColumns(key_values[0].length); //set the preferred number of columns
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
btn[i][j] = new Button(key_values[i][j]);
final int a = i;
final int b = j;
/*Add button event*/
btn[i][j].setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
calculator_screen.setText(key_values[a][b]);
}
}
);
keypad.getChildren().add(btn[i][j]);
}
}
/*Put the calculator screen and keypad into a VBox layout*/
layout.setAlignment(Pos.CENTER);
//layout.setStyle("-fx-background-color: #797983; -fx-padding: 20; -fx-font-size: 20;");
layout.getChildren().addAll(calculator_screen, keypad);
calculator_screen.prefWidthProperty().bind(keypad.widthProperty());
/*Show the window*/
stage.setTitle("Calculator");
stage.initStyle(StageStyle.UTILITY);
stage.setResizable(false);
Scene scene = new Scene(layout);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
}
![Exmaple Image]http://imgur.com/DUOU4vH
Use calculator_screen.appendText(...);
There is a simple solution.
Instead of using
calculator_screen.setText(key_values[a][b]);
use,
calculator_screen.appendText(key_values[a][b]);
Hope this will help you.

How do you call graphics class from main class in JavaFX?

I'm a really new programmer so idk if this question sounds really stupid but..
This is my main:
package culminating;
import javafx.application.Application;
& all other necessary imports...
public class CulminatingMAIN extends Application {
//Set Global variables
int count = 0;
String name;
String gender = "Boy";
Label testLabel = new Label(gender + " has been selected");
#Override
public void start(Stage primaryStage) throws Exception {
/**
* ************************ SCENE 1 WORK *************************
*/
TextField nameTextField = new TextField();
nameTextField.setMaxWidth(100);
Label nameLabel = new Label("Please enter your name.");
Label genderLabel = new Label();
Label titleLabel = new Label("Math Adventure!");
titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 30));
Rectangle titleRectangle = new Rectangle();
titleRectangle.setFill(Color.TOMATO);
titleRectangle.setWidth(280);
titleRectangle.setHeight(60);
titleRectangle.setStroke(Color.BLACK);
titleRectangle.setStrokeWidth(2.0);
StackPane root = new StackPane(titleRectangle, titleLabel);
//Set VBox properties
VBox vbox1 = new VBox(25);
vbox1.setAlignment(Pos.TOP_CENTER);
vbox1.setPadding(new Insets(60, 0, 0, 0));
vbox1.setStyle("-fx-background-color: lightskyblue");
HBox genderBtnBox = new HBox(25);
genderBtnBox.setAlignment(Pos.CENTER);
//Set Scene 1 buttons
Button enterNameBtn = new Button("Enter");
Button goToScene2Btn = new Button("Continue");
//Set Radio Button functionality here
final ToggleGroup genderGroup = new ToggleGroup();
RadioButton rb1 = new RadioButton("Boy");
rb1.setToggleGroup(genderGroup);
rb1.setUserData("Boy");
rb1.setSelected(true);
RadioButton rb2 = new RadioButton("Girl");
rb2.setToggleGroup(genderGroup);
rb2.setUserData("Girl");
//Add panes, labels and buttons to the VBox
vbox1.getChildren().addAll(root, nameLabel, nameTextField, enterNameBtn, genderLabel, genderBtnBox);
Scene scene = new Scene(vbox1, 500, 500);
primaryStage.setScene(scene);
primaryStage.setTitle("Culminating Project");
primaryStage.show();
/**
* ************************ SCENE 2 WORK *************************
*/
//THIS IS ROUGH WORK SO FAR
//Here, testing out new scene to see that it loads properly (and it does)
Circle testCircle = new Circle();
testCircle.setRadius(30);
testCircle.setFill(Color.YELLOW);
StackPane testPane = new StackPane(testCircle, testLabel);
Scene scene2 = new Scene(testPane, 500, 500);
/**
* ************************ EVENTS *************************
*/
//Stores user-entered name and prompts for user gender. Adds Continue button
enterNameBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if ((count < 1) && (!nameTextField.getText().isEmpty())) {
name = nameTextField.getText();
genderLabel.setText("Hi " + name + "! Please select whether you are a boy or girl.");
genderBtnBox.getChildren().addAll(rb1, rb2);
vbox1.getChildren().add(goToScene2Btn);
count++;
}
}
});
//When pressed, changes the scene so that scene 2 is set instead
goToScene2Btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
primaryStage.setScene(scene2);
}
});
//Radio button selection is stored in gender variable
genderGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle old_toggle, Toggle new_toggle) {
if (genderGroup.getSelectedToggle() != null) {
gender = genderGroup.getSelectedToggle().getUserData().toString();
testLabel.setText(gender + " has been selected");
}
}
});
if (gender.equals("boy")){
{
}
}
else if (gender.equals("girl")){
{
}
}
}
public static void main(String[] args) {
launch(args);
}
}
Now I have another class called CharacterGraphic, which I want to call and make the graphic I created in it appear.
package culminating;
& all the other imports
public class CharacterGraphic extends Culminating_JavaFX {
public void start(Stage primaryStage) throws Exception {
String gender = "boy";
Pane pane = new Pane();
pane.setStyle("-fx-background-color: LIGHTBLUE");
pane.setPrefSize(200, 200);
Circle head = new Circle();
head.setRadius(50);
head.setCenterX(240);
head.setCenterY(120);
head.setFill(Color.BURLYWOOD);
etc etc (all other graphics i made)
How do I do this???? And where would I do this?? Any answers really, really appreciated!

Categories