When i create 2 buttons in gridpane using the following code, it gives me 2 unequal sized buttons. How can i fix this issue ?
/**
* Set the appearance of buttons in the grid
*/
private void setButtonAppearance(Button button)
{
button.setAlignment(Pos.CENTER);
//button.setFont(font);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
button.setMinSize(Double.MIN_VALUE, Double.MIN_VALUE);
// doesn't start with focus or stay focused
button.setFocusTraversable(false);
}
/**
* Create buttons to perform allocation and deallocation of events to venues
*/
private void addButtons()
{
allocateButton = new Button("Allocate Event");
deallocateButton = new Button("Deallocate Event");
//Set appearance of buttons
setButtonAppearance(allocateButton);
setButtonAppearance(deallocateButton);
gridPane.add(allocateButton, 5, 30, 10, 1);
gridPane.add(deallocateButton, columns-15, 30, 10, 1);
}
OUTPUT:
Regards !
There are two possible solutions, either set a preferred width for the button
private void setButtonAppearance(Button button) {
button.setPrefWidth(60);
}
or set the columns which the buttons occur in to both have the same constraints. Something like this. Note, by default there aren't any ColumnConstraint objects in the the getColumnConstraints() array.
gridPane.getColumnConstraints()
.add(new ColumnConstraints(10, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true));
gridPane.getColumnConstraints()
.add(new ColumnConstraints(10, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true));
Self contained example
#Override
public void start(Stage primaryStage) throws Exception {
Button allocateButton = new Button("Allocate Event");
Button deallocateButton = new Button("Deallocate Event");
setButtonAppearance(allocateButton);
setButtonAppearance(deallocateButton);
GridPane gridPane = new GridPane();
gridPane.add(allocateButton, 0, 0, 1, 1);
gridPane.add(deallocateButton, 2, 0, 1, 1);
gridPane.getColumnConstraints()
.add(new ColumnConstraints(10, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true));
gridPane.getColumnConstraints()
.add(new ColumnConstraints(10, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true));
gridPane.getColumnConstraints()
.add(new ColumnConstraints(10, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true));
primaryStage.setScene(new Scene(gridPane));
primaryStage.show();
}
private void setButtonAppearance(Button button) {
button.setAlignment(Pos.CENTER);
// button.setFont(font);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
button.setMinSize(Double.MIN_VALUE, Double.MIN_VALUE);
// doesn't start with focus or stay focused
button.setFocusTraversable(false);
}
Either way, I'd recommend against coding JavaFX layouts by hand, instead take advantage of Scene Builder and FXML...
Related
GridPane layout = new GridPane();
Label wordInstruction = new Label("Word");
TextField wordField = new TextField();
Label translationInstruction = new Label("Translation");
TextField translationField = new TextField();
Label error = new Label();
layout.setVgap(10);
layout.setHgap(10);
layout.setPadding(new Insets(10, 10, 10, 10));
layout.setAlignment(Pos.CENTER);
Button addButton = new Button("Add");
// Stops the button from consuming mouse events and allows it to become the default button.
addButton.setSkin(new ButtonSkin(addButton) {
{
this.consumeMouseEvents(false);
}
});
// The button can now be pressed with the enter key.
addButton.setDefaultButton(true);
layout.add(wordInstruction, 0, 0);
layout.add(wordField, 0, 1);
layout.add(translationInstruction, 0, 2);
layout.add(translationField, 0, 3);
layout.add(addButton, 0, 4);
layout.add(error, 0, 6);
addButton.setOnAction((event) -> {
String word = wordField.getText();
String translation = translationField.getText();
if (!translationField.getText().isEmpty()) {
this.dictionary.add(word.toLowerCase(), translation.toLowerCase());
error.setText("");
wordField.clear();
} else {
error.setText("Please input a translation.");
}
translationField.clear();
});
return layout;
}
Hello, I've just started using JavaFX and I've not been able to find anything in the documentation related to my problem. When I press the addButton, I want the user to be able to write into the wordField textField straight away instead of having to click it or tab all the way to it. Is there any way to make the textField active in my addButton.setOnAction function?
Thank you in advance.
How about running the TextField.requestFocus() method?
I'm trying to Use reactive bindings to bind the value of alcoholPercentageField to the progress property of alcoholBar.
The progress bar will "full" when alcoholic content is set to 20 % and empty when the alcoholic content is 0
My Code-
public void start(Stage stage) throws Exception {
stage.setTitle("Mead calculator");
// Creating the fields and components
TextField waterAmountField = new TextField();
TextField alcoholPercentageField = new TextField();
TextField sugarAmountField = new TextField();
Label meadTotalAmount = new Label();
Label assessmentLabel = new Label("");
//assessmentLabel.textProperty().bind(alcoholPercentageField.textProperty());
//Conditional Binding Error
assessmentLabel.textProperty().bind(Bindings.when((alcoholPercentageField.textProperty().lessThan(5))).then("Smart").otherwise("Bad"));
ProgressBar alcoholBar = new ProgressBar();
//Error is here
alcoholBar.progressProperty().bind(alcoholPercentageField.textProperty() * 5);
Rest of the Code: some visual things
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
var columnOneConstraints = new ColumnConstraints(150, 150, Double.MAX_VALUE);
columnOneConstraints.setHalignment(HPos.RIGHT);
var columnTwoConstrains = new ColumnConstraints(200,200, Double.MAX_VALUE);
columnTwoConstrains.setHgrow(Priority.SOMETIMES);
grid.getColumnConstraints().addAll(columnOneConstraints, columnTwoConstrains);
alcoholBar.setMaxWidth(Double.MAX_VALUE);
GridPane.setColumnSpan(alcoholBar, 2);
GridPane.setHalignment(assessmentLabel, HPos.RIGHT);
sugarAmountField.setDisable(true);
grid.add(new Label("Water (l):"), 0, 0);
grid.add(waterAmountField, 1, 0);
grid.add(new Label("Vol-%:"), 0, 1);
grid.add(alcoholPercentageField, 1, 1);
grid.add(new Label("Sugar (kg):"), 0, 2);
grid.add(sugarAmountField, 1, 2);
grid.add(new Label("Lemons: "), 0, 3);
grid.add(new Label("To taste"), 1, 3);
grid.add(new Label("Mead total (kg):"), 0, 4);
grid.add(meadTotalAmount, 1, 4);
grid.add(alcoholBar, 0, 5);
grid.add(assessmentLabel, 1, 6);
// Okay, layout creation stops here
// And of course set the scene and show the stage as always
stage.setScene(new Scene(grid, 500, 400));
stage.show();
}
}
You need to create some additional Property and Binding objects.
First, create properties for your TextField alcoholPercentageField. Then, you'll bind them using a StringConverter to convert the text entered into doubles.
Finally, use bindBidirectional to propagate and lastly bind it with progressBar by dividing 20 ( The progress bar will be "full" when alcoholic content is set to 20 %)
Code Will be like this-
// Properties used for bindings
DoubleProperty alcoholPercent = new SimpleDoubleProperty();
//Setup the converters to get the input from the textfields
StringConverter<? extends Number> converter = new DoubleStringConverter();
Bindings.bindBidirectional(alcoholPercentageField.textProperty(), alcoholPercent, (StringConverter<Number>) converter);
alcoholBar.progressProperty().bind(alcoholPercent.divide(20));
For Conditional Binding, check this Conditional Binding
Code would be like this-
assessmentLabel.textProperty().bind(Bindings.when(alcoholPercent.lessThan(5)).then("Smart").otherwise("Bad"));
assessmentLabel.textFillProperty().bind(Bindings.when(alcoholPercent.lessThan(5)).then(Color.GREEN).otherwise(Color.RED));
Use the Bindings API.
Note that the progress is supposed to be between 0 and 1, so if you are entering percentages, instead of proportions, into your text field you need to divide by 100:
alcoholBar.progressProperty().bind(Bindings.createDoubleBinding(
() -> 0.05 * Double.parseDouble(alcoholPercentageField.getText()),
alcoholPercentageField.textProperty()
));
You might want to implement more complex logic to, e.g. check for a valid number, or at least non-empty text field.
Im calling a new stage in my program which I like to close on pressing escape. I did this which gives me a NullPointerException:
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
KeyCode key = t.getCode();
if (key == KeyCode.ESCAPE){
stage.close();
}
}
});
try this..
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>
() {
#Override
public void handle(KeyEvent t) {
if(t.getCode()==KeyCode.ESCAPE)
{
System.out.println("click on escape");
Stage sb = (Stage)label.getScene().getWindow();//use any one object
sb.close();
}
}
});
Add the event handler to the stage/window you want to close on ESC.
JavaFX 8 style:
stage.addEventHandler(KeyEvent.KEY_RELEASED, (KeyEvent event) -> {
if (KeyCode.ESCAPE == event.getCode()) {
stage.close();
}
});
why you dont show us more of your code? However, try this:
public class Login extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("JavaFX Welcome");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text scenetitle = new Text("Welcome");
scenetitle.setId("welcome-text");
grid.add(scenetitle, 0, 0, 2, 1);
Label userName = new Label("User Name:");
grid.add(userName, 0, 1);
TextField userTextField = new TextField();
grid.add(userTextField, 1, 1);
Label pw = new Label("Password:");
grid.add(pw, 0, 2);
PasswordField pwBox = new PasswordField();
grid.add(pwBox, 1, 2);
Button btn = new Button("Sign in");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(btn);
grid.add(hbBtn, 1, 4);
Scene scene = new Scene(grid, 660, 300);
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
if (ke.getCode() == KeyCode.ESCAPE) {
System.out.println("Key Pressed: " + ke.getCode());
primaryStage.close();
}
}
});
//primaryStage.setFullScreen(true);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Done.
Hope the imported Eventhandler package is not of JavaFX.
Try using,
import javafx.event.EventHandler;
Note:As the naming convention are same but only the package name differs between swing and JavaFX, Your NetBeans/Eclipse may auto import different packages, so make sure of it.
I'm fairly new to programming, and I've been working on a two player Pong game with JavaFX. One player uses W/S to move his "paddle" and the other player uses the Up/Down arrows for his. The problem I keep having is that only one player can move his paddle at a time. I can't figure out how to get it so they can each move their individual paddles at the same time. I had one keyboard event handler control both paddles, I thought that was the problem. I made two separate keyboard handlers, but got another set of problems tat I think is caused by the built in setFocusTraversable methods. I hope what I'm trying to do makes sense. Any ideas?
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
public class Game{
Rectangle leftPaddle;
double leftPaddleY = 260;
Rectangle rightPaddle;
double rightPaddleY = 260;
public void createGame(Group gameDisplay){
//creates background
Rectangle background = new Rectangle(0,0,800,600);
background.getStyleClass().add("background");
//draws field lines
Canvas game = new Canvas(800, 600);
GraphicsContext gc = game.getGraphicsContext2D();
gc.setStroke(Paint.valueOf("WHITE"));
gc.setLineWidth(5);
gc.strokeLine(400, 0, 400, 600);
gc.strokeOval(300, 200, 200, 200);
gc.strokeRect(0, 150, 100, 300);
gc.strokeRect(700, 150, 100, 300);
gc.setStroke(Paint.valueOf("BLACK"));
gc.setLineWidth(8);
gc.strokeRect(0, 0, 800, 600);
//creates red paddle
leftPaddle = new Rectangle(30, leftPaddleY, 20, 70);
leftPaddle.setOnKeyPressed(paddleMovement);
leftPaddle.setFocusTraversable(true);
leftPaddle.setFill(Color.RED);
//creates blue paddle
rightPaddle = new Rectangle(750, rightPaddleY, 20, 70);
rightPaddle.setOnKeyPressed(paddleMovement);
rightPaddle.setFocusTraversable(true);
rightPaddle.setFill(Color.BLUE);
gameDisplay.getStylesheets().add(getClass().getResource("GameDisplay.css").toExternalForm());
gameDisplay.getChildren().addAll(background, game, leftPaddle, rightPaddle);
}
public EventHandler<KeyEvent> paddleMovement = new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event) {
//red paddle movement
if(event.getCode().equals(KeyCode.W)){
leftPaddle.setY(leftPaddleY -= 6);
if(leftPaddle.getY() < 0){
leftPaddle.setY(0);
leftPaddleY = 0;
}
}
if(event.getCode().equals(KeyCode.S)){
leftPaddle.setY(leftPaddleY += 6);
if(leftPaddle.getY() < 0){
leftPaddle.setY(0);
leftPaddleY = 0;
}
}
//blue paddle movement
if(event.getCode().equals(KeyCode.UP)){
rightPaddle.setY(rightPaddleY -= 6);
if(rightPaddle.getY() < 0){
rightPaddle.setY(0);
rightPaddleY = 0;
}
}
if(event.getCode().equals(KeyCode.DOWN)){
rightPaddle.setY(rightPaddleY += 6);
if(rightPaddle.getY() < 0){
rightPaddle.setY(0);
rightPaddleY = 0;
}
}
}
};
}
The keyPressed event will only be triggered repeatedly for the last key pressed.
To work around this, just "remember" the desired movement for each paddle and listen to the keyReleased event too. You can execute the movements using a AnimationTimer to execute the updates every frame (you could also use a Timeline instead to have more control over the frequency of the updates).
Furthermore I recommend being a bit more restrictive with the visibility of your member variables, since usually you do not want other classes to be able to directly write to the fields of your class. Also I recommend handling the events in a single Node. Only one Node can have focus at a time and handling the event in different places just results in duplicate code.
public class Game {
private Rectangle leftPaddle;
private double leftPaddleY = 260;
private Rectangle rightPaddle;
private double rightPaddleY = 260;
private double leftPaddleDY;
private double rightPaddleDY;
private AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
// update paddle positions
leftPaddleY += leftPaddleDY;
rightPaddleY += rightPaddleDY;
if (leftPaddleY < 0) {
leftPaddleY = 0;
}
if (rightPaddleY < 0) {
rightPaddleY = 0;
}
leftPaddle.setY(leftPaddleY);
rightPaddle.setY(rightPaddleY);
}
};
public void createGame(Group gameDisplay) {
//creates background
Rectangle background = new Rectangle(0, 0, 800, 600);
background.getStyleClass().add("background");
//draws field lines
Canvas game = new Canvas(800, 600);
GraphicsContext gc = game.getGraphicsContext2D();
gc.setStroke(Paint.valueOf("WHITE"));
gc.setLineWidth(5);
gc.strokeLine(400, 0, 400, 600);
gc.strokeOval(300, 200, 200, 200);
gc.strokeRect(0, 150, 100, 300);
gc.strokeRect(700, 150, 100, 300);
gc.setStroke(Paint.valueOf("BLACK"));
gc.setLineWidth(8);
gc.strokeRect(0, 0, 800, 600);
//creates red paddle
leftPaddle = new Rectangle(30, leftPaddleY, 20, 70);
leftPaddle.setFill(Color.RED);
//creates blue paddle
rightPaddle = new Rectangle(750, rightPaddleY, 20, 70);
rightPaddle.setFill(Color.BLUE);
// register event handlers to Canvas
game.setFocusTraversable(true);
game.setOnKeyPressed(keyPressed);
game.setOnKeyReleased(keyReleased);
gameDisplay.getStylesheets().add(getClass().getResource("GameDisplay.css").toExternalForm());
gameDisplay.getChildren().addAll(background, game, leftPaddle, rightPaddle);
// start updates of paddle positions
timer.start();
}
private EventHandler<KeyEvent> keyReleased = new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
// set movement to 0, if the released key was responsible for the paddle
switch (event.getCode()) {
case W:
case S:
leftPaddleDY = 0;
break;
case UP:
case DOWN:
rightPaddleDY = 0;
break;
}
}
};
private EventHandler<KeyEvent> keyPressed = new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
// start movement according to key pressed
switch (event.getCode()) {
case W:
leftPaddleDY = -6;
break;
case S:
leftPaddleDY = 6;
break;
case UP:
rightPaddleDY = -6;
break;
case DOWN:
rightPaddleDY = 6;
break;
}
}
};
}
i am new in JavaFX programming. I have an Application, with a simple login page as described in the example here, and i add a StringProperty to the actiontarget element. So when the text changes inside the actiontarget i want a new FXML file with a webview inside, to load from the FXMLLoader and be dipslayed on the screen. Below is the exception i get. I can load any other fxml file, without a webview inside it, without a problem. Thanks in advance.Code samples below
The exception :
java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
at com.sun.webpane.sg.prism.InvokerImpl.checkEventThread(InvokerImpl.java:33)
at com.sun.webpane.platform.WebPage.<init>(WebPage.java:189)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:57)
at com.sun.webpane.sg.ImplementationManager.createPage(ImplementationManager.java:51)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:704)
at javafx.scene.web.WebEngine.<init>(WebEngine.java:691)
at javafx.scene.web.WebView.<init>(WebView.java:245)
at student.WebBrowser.<init>(WebBrowser.java:31)
at Login.Login.replaceSceneContent(Login.java:171)
at Login.Login.access$000(Login.java:66)
at Login.Login$2.changed(Login.java:143)
at Login.Login$2.changed(Login.java:137)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at Login.Client.run(Client.java:66)
First my listener:
// Add change listener
sp.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
if(t1.equalsIgnoreCase("user authenticated successfully")){
try {
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
System.out.println("everything ok");
} catch (Exception ex) {
System.out.println("something went wrong");
ex.printStackTrace();
}
}
}
});
Second my method : replaceSceneContent(String fxml, Stage stage)
private Parent replaceSceneContent(String fxml, Stage stage) throws Exception {
Parent page = (Parent) FXMLLoader.load(getClass().getResource("/FXML_Files/"+fxml), null, new JavaFXBuilderFactory());
Scene scene = stage.getScene();
if (scene == null) {
scene = new Scene(page, 700, 450);
stage.setScene(scene);
} else {
stage.getScene().setRoot(page);
}
if(fxml.equalsIgnoreCase("Student.fxml")){
Pane spane = (Pane) page.lookup("#pane");
WebBrowser wb = new WebBrowser();
spane.getChildren().add(wb);
}
return page;
}
And my WebBrowser class similar to the example in NetBeans7.2:
public class WebBrowser extends Pane {
public WebBrowser() {
WebView view;
final WebEngine eng;
view = new WebView();
view.setMinSize(10, 10);
view.setPrefSize(500, 400);
eng = view.getEngine();
eng.load("http://www.oracle.com/us/index.html");
VBox.setVgrow(this, Priority.ALWAYS);
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
final TextField locationField = new TextField("http://www.oracle.com/us/index.html");
locationField.setMaxHeight(Double.MAX_VALUE);
Button goButton = new Button("Go");
goButton.setDefaultButton(true);
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.load(locationField.getText().startsWith("http://") ? locationField.getText() :
"http://" + locationField.getText());
}
};
goButton.setOnAction(goAction);
locationField.setOnAction(goAction);
eng.locationProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
locationField.setText(newValue);
}
});
GridPane grid = new GridPane();
ButtonsEvents be = new ButtonsEvents();
TilePane tp = be;
tp.setAlignment(Pos.CENTER);
grid.setVgap(5);
grid.setHgap(5);
GridPane.setConstraints(locationField, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.SOMETIMES);
GridPane.setConstraints(goButton,1,0);
GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
GridPane.setConstraints(tp, 0, 2, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.NEVER);
grid.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true),
new ColumnConstraints(40, 40, 40, Priority.ALWAYS, HPos.CENTER, true)
);
grid.getChildren().addAll(locationField, goButton,view, tp);
getChildren().add(grid);
}
#Override
protected void layoutChildren() {
List<Node> managed = getManagedChildren();
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
for (int i = 0; i < managed.size(); i++) {
Node child = managed.get(i);
layoutInArea(child, left, top,
width - left - right, height - top - bottom,
0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
}
}
}
As stated in the exception, changes in the JavaFX scene graph can only be made in the JavaFX application thread. To make sure your code is run in this thread, try the following:
Platform.runLater(new Runnable() {
#Override
public void run() {
// This method is invoked on JavaFX thread
replaceSceneContent(cb.getSelectionModel().getSelectedItem().toString()+".fxml",primaryStage);
}
});
The javafx manager will run this code at some point in the future on the correct thread.
More information:
http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm