How to save/store variables after clicking a button in javafx - java

I am super new to JavaFX, with some knowledge of Java tho.
My target is to develop a desktop broad game.
It should ask players for certain options and determine how the game would proceed (like voting, selection rooms, etc)
It works perfectly in Java using Scanner but not in JavaFX;
Whenever I click a button, it only affect the variable in the block.
I do need these chosen options to be used later to support the game logic.
Please help.
Updated - I move the if statement into the action block but it still not set scene to welcome.
public class Main extends Application{
Button startGame,howTOPlay, viewRoomButton, viewItemListButton;
List<Scene> playersenes = new ArrayList<>();
Scene welcomeScene, playerRedScene, playerYellowScene, playerBlueScene, playerGreenScene, playerBrownScene, playerBlackScene;
Stage mainWindow;
static int numberOfPlayers;
final static int WIDTH =800;
final static int HEIGHT=600;
;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
//mainWidow setup
mainWindow = primaryStage;
mainWindow.setTitle("Mall of Horror");
//startgameScene
List<Integer> numberOfPlayersOptions = new ArrayList<>();
numberOfPlayersOptions.add(4);
numberOfPlayersOptions.add(5);
numberOfPlayersOptions.add(6);
GameBroad gameBroad=new GameBroad(1);
PlayersChoice playersChoice = new PlayersChoice();
startGame = new Button();
startGame.setText("Start Game");
startGame.setOnAction(event -> {
//numberwindow.display is a new stage with a method return a static int
//gamebroad is class with models, which require the number of players to support its logic.
gameBroad.setPlayersNumber(NumberWindow.display(numberOfPlayersOptions, "please select how many players"));
//playerChoise is a class, ok is default fault boolean. set Ok is method to set the ok boolean to true;
playersChoice.setOk();
mainWindow.setScene(welcomeScene);
if (playersChoice.isOk()){//it stop working here
//welcomeScene;
Label welcome = new Label();
welcome.textProperty().setValue("Welcome players " + gameBroad.getPlayersNumber() );
Button ok1 = new Button("OK");
ok1.setOnAction(event1 -> {
mainWindow.setScene(playerRedScene);
});
VBox welcomePlayerslayout = new VBox();
welcomePlayerslayout.getChildren().addAll(welcome,ok1);
welcomeScene = new Scene(welcomePlayerslayout, WIDTH, HEIGHT);
//playerRedScene;
viewRoomButton = new Button();
viewRoomButton.setText("Rooms");
viewItemListButton = new Button();
viewItemListButton.setText("Items");
VBox playerRedLayout = new VBox();
playerRedLayout.getChildren().addAll(viewRoomButton,viewItemListButton);
playerRedScene = new Scene(playerRedLayout, 800,600);
}
System.out.println(playersChoice.isOk()); //it shows true here;
});
howTOPlay = new Button();
howTOPlay.setText("How to Play");
VBox firstlayout = new VBox();
firstlayout.getChildren().addAll(startGame, howTOPlay);
Scene firstscene = new Scene(firstlayout, WIDTH, HEIGHT);
primaryStage.setScene(firstscene);
mainWindow.show();
}
}

if (playersChoice.isOk()){//it stop working here
It did not stop working there. It didn't start to work. It defaults to false until you click startGame, and the action does not check again if the choice is okay.
Therefore, you need to setup your welcome actions within the start button action, which only runs after you click the button.
In the current code, that if statement is ran before the button click despite the code having been written below the action. In other words, GUI programming does not explicitly run top to bottom.
Your next issue is that you have mainWindow.setScene(welcomeScene); before welcomeScene = new Scene(welcomePlayerslayout, WIDTH, HEIGHT);

Related

Codename One dragged-over container quickly dissappears

I have an Android application that uses drag and drop operations. I haven't used drag and drop often, so I am just starting to learn how they work and what the implications might be.
In my app, whenever I drop the component over the target, the target sort of "flashes", meaning it disappears for a brief moment and than reappears again.
Here a link to the video with the app in its current stage.
I suppose this is due to the animation of the form. If so, how can I disable it, or stop the target from flashing?
Below is my code. It is still rudimentary, since I just started to work on the project.
public void testDrag () {
Container containerDropTarget = new Container();
Container container = new Container(new GridLayout(5,1));
Label label = new Label("test test test test test test");
Button buttonTwo = new Button("Test");
buttonTwo.addDragOverListener(l-> {
containerDropTarget.setUIID("DialogTest");
});
containerDropTarget.setUIID("LetterHolder");
buttonTwo.setDraggable(true);
containerDropTarget.setDropTarget(true);
container.add(label).add(containerDropTarget);
form.add(container).add(buttonTwo);
form.show();
}
I don't have the UIIDs defined so I used this code based on your test case and it worked correctly. I also added a label to the drop container so it could be found:
Container containerDropTarget = new Container() {
#Override
public void drop(Component dragged, int x, int y) {
super.drop(dragged, x, y);
setUIID("Container");
}
};
containerDropTarget.add(new Label("Drop Target"));
Form form = new Form("Test Drag", BoxLayout.y());
Container container = new Container(new GridLayout(5,1));
Label label = new Label("test test test test test test");
Button buttonTwo = new Button("Test");
buttonTwo.addDragOverListener(l-> {
//containerDropTarget.setUIID("DialogTest");
containerDropTarget.getAllStyles().setBgColor(0xff0000);
containerDropTarget.getAllStyles().setBgTransparency(0xff);
containerDropTarget.repaint();
});
containerDropTarget.setUIID("LetterHolder");
buttonTwo.setDraggable(true);
containerDropTarget.setDropTarget(true);
container.add(label).add(containerDropTarget);
form.add(container).add(buttonTwo);
form.show();

How to set up your init method for a mobile application

For a better explanation it is here. I have recently started using Gluon to create mobile applications and I am running into some issues when I created the MainMenu.java file. I am unable to configure the init method and keep receiving errors like "String cannot be converted to a stage" and unsure how to fix this. It would be much appreciated for the help I am unsure how to proceed. I keep getting the error:
error: incompatible types: invalid constructor reference
addViewFactory(HOME_VIEW, LoginPage::new);
^
constructor LoginPage in class LoginPage cannot be applied to given types required: Stage
found: no arguments
reason: actual and formal argument lists differ in length
I have tried to change the addViewFactory method and nothing as has worked, including placing primaryStage inside the last ().
This is the file I am receiving trouble with
public class AlexDemo extends MobileApplication {
public static final String LOGIN_VIEW = HOME_VIEW;
#Override
public void init() {
addViewFactory(LOGIN_VIEW, () -> new LoginPage(LOGIN_VIEW));
}
#Override
public void postInit(Scene scene) {
Swatch.BLUE.assignTo(scene);
((Stage) scene.getWindow()).getIcons().add(new Image(AlexDemo.class.getResourceAsStream("/icon.png")));
}
}
This is the login page with code that works(omly showed some I have way too much with buttons, labels etc)
public class LoginPage extends View {
private Parent root;
public LoginPage(Stage primaryStage) {
Scene scene = new Scene(root, 500, 800);
BorderPane root = new BorderPane();
//For the label displaying "Sign In" to prompt the user
VBox login = new VBox();
Label statement = new Label("Sign In");
statement.setFont(Font.font("Verdana", FontWeight.BOLD, 13));
//HBox for the email the user wants to sign in with
HBox firstUserPrompt = new HBox();
Label email = new Label("Username/\nEmail: ");
email.setFont(Font.font("Verdana", 13));
firstUserPrompt.setAlignment(Pos.CENTER);
TextField userPrompt = new TextField();
}
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setTitleText("Book App");
appBar.setSpacing(115);
}
}
This is the MainMenu page on the click of the button
public class MainMenu extends View{
private Parent prevPage;
private Stage stage;
public MainMenu(Parent LoginPage, Stage stage){
this.prevPage = LoginPage;
this.stage = stage;
Label statement = new Label("Sign In");
}
It does not allow me to run the application anymore after creating the MainMenu.java file and it is occurring within the first piece of code.
The signature of addViewFactory is
void addViewFactory(java.lang.String viewName, java.util.function.Supplier<View> supplier)
i.e. using a method reference for the second parameter you need to use a method (or constructor) taking 0 parameters. There is no constructor for LoginPage that fulfills this requirement; therefore the compiler does not accept LoginPage::new.
As for the errors in the constructors of your subclasses of View:
View does not provide a constructor taking no parameters. For this reason you need to invoke one of the constructors of View explicitly at the start of each of your constructors (as long as you do not use constructor chaining) using super(<args>); as the first statement in the constructor.
Example
public MainMenu(Parent LoginPage, Stage stage) {
super(LoginPage); // use View(javafx.scene.Node content)
this.prevPage = LoginPage;
this.stage = stage;
Label statement = new Label("Sign In");
}
private LoginPage(BorderPane root) {
super(root);
//For the label displaying "Sign In" to prompt the user
VBox login = new VBox();
Label statement = new Label("Sign In");
statement.setFont(Font.font("Verdana", FontWeight.BOLD, 13));
//HBox for the email the user wants to sign in with
HBox firstUserPrompt = new HBox();
Label email = new Label("Username/\nEmail: ");
email.setFont(Font.font("Verdana", 13));
firstUserPrompt.setAlignment(Pos.CENTER);
TextField userPrompt = new TextField();
// TODO: add something to root???
}
public LoginPage() {
this(new BorderPane());
}
Note that passing a Stage makes little sense, since it's the responisbility of MobileApplication deal with Scene/Stage creation.

Is it possible to return a variable in javaFX from a close method? Is there a more efficient way to do this?

I would like to run a method that would close a pop up window when an answer is selected or the window is closed manually and then return the value selected (a boolean). Unfortunately, I do not know how I would retreive the data because I initially call the method that displays the query, not the close method.
I have tried many different things but when I try to use a close method I am unable to retrieve the data returned from it. Otherwise, the data returns before the user is able to mutate it. Neither result achieves my goal
This is my method that creates the box. I currently just close the window instead of closing via an alternate closing method.
public class ConfirmBox
{
static boolean answer;
public static boolean display(String title, String question) {
Stage window = new Stage();
//Block events to other windows
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(title);
window.setMinWidth(250);
//LabelQ
Label label = new Label();
label.setText(question);
//YesBox
Button yesB= new Button("Yes");
yesB.getStyleClass().add("button-green");
yesB.setOnAction(e -> {
answer=true;
window.close();
});
//NoBox
Button noB= new Button("No");
noB.getStyleClass().add("button-red");
yesB.setOnAction(e -> {
answer=false;
window.close();
});
window.setOnCloseRequest(e ->{
e.consume();
window.close();
});
VBox layout = new VBox(10);
layout.getChildren().addAll(label, yesB, noB);
layout.setAlignment(Pos.CENTER);
//Display window and wait for it to be closed before returning
Scene scene = new Scene(layout, 800, 200);
scene.getStylesheets().add("styles.css");
window.setScene(scene);
window.showAndWait();
return answer;
}
}
This is my method that runs everything thus far.
public class Main extends Application
{
Stage window;
String gender;
String name;
public static void main (String[] args){
launch (args);
}
public void start(Stage primaryStage) throws Exception{
window= primaryStage;
window.setTitle("Pokemon Gray");
GridPane grid= new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(8);
grid.setHgap(10);
// Name Label
Label nameLabel= new Label("UserName");
GridPane.setConstraints(nameLabel,0, 0);
//Name input
TextField nameInput = new TextField();
GridPane.setConstraints(nameInput,1, 0);
//GenderLabel
Label genderLabel= new Label("Gender");
GridPane.setConstraints(genderLabel,0, 1);
//Gender Boy
Button boySelect= new Button("Boy");
boySelect.getStyleClass().add("button-blue");
GridPane.setConstraints(boySelect, 1, 1);
boySelect.setOnAction(e-> gender="Male");
//Gender Girl
Button girlSelect= new Button("Girl");
girlSelect.getStyleClass().add("button-red");
GridPane.setConstraints(girlSelect, 2, 1);
girlSelect.setOnAction(e-> gender="Female");
//Login
Button saveName= new Button("Save Name");
GridPane.setConstraints(saveName, 1, 2);
saveName.setOnAction( e -> {
boolean valid =verifyText(nameInput.getText());
if ((valid)&&((gender == "Male")||(gender=="Female"))){
String qStr= "Are you sure your name is "+nameInput.getText()+" and you are a "+gender+"?";
boolean confirmed= ConfirmBox.display("Confirmation", qStr );
if (confirmed == true){
name= nameInput.getText();
}
}
else{
ErrorBox.display("Error", "You must enter a name less than 20 characters long and a gender");
}
});
grid.getChildren().addAll(nameLabel, nameInput, genderLabel, boySelect, girlSelect, saveName);
Scene scene= new Scene(grid, 1000, 1000);
scene.getStylesheets().add("styles.css");
window.setScene(scene);
window.show();
}
public boolean verifyText(String text){
boolean valid;
if ((text.length() > 0) && (text.length() <= 20)){
valid= true;
}
else{
valid=false;
}
return valid;
}
}
I want to be able to run a seperate method that closes it and returns the answer boolean in the ConfirmBox to the Main method. But currently it returns null because it returns immediately.
Edit: It should be noted I currently do not have a close method. I deleted my previous one because it caused many issues.
I think that you can't do that, you need to do another screen like:
If(button.getResult==yes){"charge yesscreen")else{"charge no screen"}
The controllers for differents screens can't interectionate with another screens, you only can call the differents screens from to a controller but you can't pass values with hims, try do something like this I work with javafx with this mentality all screens are independents and you only invoce the differents screens . you can do something like this:
class first screen{
is a girl?
is a charizar?
is a x)
if(isgirl && is a x...){
call screen x
}else if(ismen&& is a z..){
call screen z
}
{

How can I change the GUI of JavaFX outside start()?

I am crazy about the feature of JavaFX, in Swing, I could do,
#Override
public void onPluginRegistered(final GamePlugin plugin) {
JRadioButtonMenuItem gameMenuItem = new JRadioButtonMenuItem(plugin.getGameName());
gameMenuItem.setSelected(false);
gameMenuItem.addActionListener(event -> {
if (core.getPlayers().isEmpty()) {
// Can't start a game with no players.
showErrorDialog(frame, ERROR_NO_PLAYERS_TITLE, ERROR_NO_PLAYERS_MSG);
gameGroup.clearSelection();
} else {
core.startNewGame(plugin);
}
});
gameGroup.add(gameMenuItem);
newGameMenu.add(gameMenuItem);
}
if I want to add a radio item whenever a plugin has registered.
However in JavaFX, it seems, you can't declare any global item of JavaFX, because once the start() is called, it starts a new constructor and everything you've done before is nothing (there is no variable share to me).
Here is my Javafx code.
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 500, 500);
scene.getStylesheets().add("./Buttons.css");
Region spacer = new Region();
spacer.setMinWidth(10);
primaryStage.setScene(scene);
primaryStage.show();
TabPane tabPane = new TabPane();
Tab tabData = new Tab("Get your data");
tabPane.getTabs().add(tabData);
Tab tabDisplay = new Tab("Visualize your data");
tabPane.getTabs().add(tabDisplay);
pluginGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>(){
#Override
public void changed(ObservableValue<? extends Toggle> ov,
Toggle old_toggle, Toggle new_toggle) {
if (pluginGroup.getSelectedToggle() != null) {
RadioButton chk = (RadioButton) new_toggle.getToggleGroup().getSelectedToggle();
chk.getText();
}
}
});
root.setCenter(tabPane);
FlowPane inputPanel = new FlowPane();
TextField source = new TextField ();
Button confirmButton = new Button("Get Your Resource!");
confirmButton.getStyleClass().add("GREEN");
inputPanel.getChildren().addAll(new Label("Input your source:"),
spacer, source, confirmButton);
root.setBottom(inputPanel);
RadioButton defaultBtn = new RadioButton("No data plugin are registered");
FlowPane pane = new FlowPane();
pane.getChildren().addAll(new Label("Select your data source"), spacer);
if (radioButtonBox != null) {
pane.getChildren().add(radioButtonBox);
}
tabData.setContent(pane);
}
#Override
public void onPluginRegistered(DataPlugin plugin) {
RadioButton button = new RadioButton(plugin.getName());
button.setToggleGroup(pluginGroup);
radioButtonBox.getChildren().add(button);
}
public void caller(String[] args) {
launch(args);
}
I want to initialize the javafx program from,
public static void main(String[] args) throws Exception {
DataFramework core = new ConcreteDataFramework();
GuiFramework gui = new GuiFramework(core);
core.addGuiListener(gui);
gui.caller(args);
core.registerPlugin(new CsvData());
}
It is weird that I can't add any radio button to the existing radioButtonBox every time I call onPluginRegistered(DataPlugin plugin) (The new radiobutton does not show up)
You should consider the start() method as the replacement for the main method. If your application needs access to some kind of service or model, create it in the start() (or init()) method. I would actually recommend making the Application subclass (which is inherently not reusable) as minimal as possible - it should just do the startup work - and factoring the remaining GUI code into a separate class. (If you use FXML, the FXML file can define the UI, and the Application subclass is then already pretty minimal: it just loads and displays the FXML.)
You haven't really provided enough context to make it clear what's going on here, but I'm guessing GuiFramework is the Application subclass you've shown part of, and DataFramework is an interface of some kind. I also assume GuiFramework is implementing some interface that defines the onPluginRegistered method.
So I would do:
public class GuiFramework implements PluginAware {
private final BorderPane root ;
private final DataFramework dataFramework ;
public GuiFramework(DataFramework dataFramework) {
this.dataframework = dataFramework ;
this.root = new BorderPane();
TabPane tabPane = new TabPane();
Tab tabData = new Tab("Get your data");
tabPane.getTabs().add(tabData);
// etc etc (remaining code from your start() method)
}
public Parent getView() {
return root ;
}
#Override
public void onPluginRegistered(DataPlugin plugin) {
RadioButton button = new RadioButton(plugin.getName());
button.setToggleGroup(pluginGroup);
radioButtonBox.getChildren().add(button);
}
}
and define a Main class for starting the application:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
DataFramework core = new ConcreteDataFramework();
GuiFramework gui = new GuiFramework(core);
core.addGuiListener(gui);
Scene scene = new Scene(gui.getView(), 500, 500);
scene.getStylesheets().add("./Buttons.css");
primaryStage.setScene(scene);
primaryStage.show();
core.registerPlugin(new CsvData());
}
// for environments not supporting JavaFX launch automatically:
public static void main(String[] args) {
launch(args);
}
}

Text box with data validation

I want to ask the user for their gender. I want to create a text box that they can answer the question in. The do-while loop is to ensure they answer with either "boy" or "girl". There are no errors but it won't run.
Note I have all the necessary imports...
public class Culminating_JavaFX extends Application {
String gender;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
TextField textField = new TextField ();
do
{
textField.setPromptText("Are you a boy or a girl?");
textField.setText("");
gender = br.readLine().toLowerCase();
}
while (!(gender.equals("boy")) && !(gender.equals("girl")));
GridPane.setConstraints(textField, 0, 1);
grid.getChildren().add(textField);
}
public static void main(String [] args) {
launch(args);
}
}
public class Culminating_JavaFX extends Application {
private GridPane grid = new GridPane();
private TextField textField = new TextField();
private Label label = new Label("Are you boy or girl?");
private Button btn;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
btn = new Button();
btn.setText("Answer");
// set action listener -> runs when button is pressed
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// process the form
process();
}
});
// set constraints
GridPane.setConstraints(textField, 0, 0);
GridPane.setConstraints(label, 0, 1);
GridPane.setConstraints(btn, 0, 2);
// add components to grid
grid.getChildren().add(textField);
grid.getChildren().add(label);
grid.getChildren().add(btn);
// show scene
primaryStage.setScene(new Scene(grid, 300, 250));
primaryStage.show();
}
private void process() {
// get text
String text = textField.getText();
// process text
if (text.equals("boy")) {
label.setText("You are a boy.");
} else if (text.equals("girl")) {
label.setText("You are a girl.");
}
}}
image of required imports
I wrote a short example, please check it above. Your program goes into do-while loop and stays there. It never gets to the point where it would draw the window and components. That's why it doesn't run.
On a side note, make sure from now on you try to keep your logic code and your graphical user interface code as separate as possible. Never try to cram everything into the GUI class.
Next thing is that the general idea of GUI's is that their logic can't be tied up in loops before being ran. When your program runs and calls start(), it will proceed downwards and execute code and needs to hit a line called window.show();. This displays the window to the user. If your program is stuck in that loop above, it won't ever be able to even display the GUI to the user and thus won't work.
Instead, rethink how your program will work. Since the user needs to select boy or girl, why not use a ChoiceBox for that, or better yet, a RadioButton. Have the user select the choice they want, then perhaps have a Button for them to click to submit or have the ChoiceBox or RadioButton listen for changes by calling:
yourRadioButton.setOnAction(e ->
{
/*
* Set the Boy Girl value here by calling
* yourRadioButton.getValue()
*/
}

Categories

Resources