I am looking to have buttons play in the order in which they are clicked. For example, if I click buttons 3, 4, and 6 I would like to then click a "play" button and this button then clicks the same pattern I clicked.
This piece of code makes it looks like the button was pressed
button.arm();
PauseTransition pause = new PauseTransition(Duration.seconds(0.5));
pause.setOnFinished(e -> {
button.disarm();
});
pause.play();
If you want the button to actually fire not just look like it did then add button.fire(); after the button.disarm();
Then you just need to record the buttons that are pressed
Here is a crude example of how to implement this. TimeLine and SequentialTransitions are the key. Comments are in the code.
import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.SequentialTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXApplication57 extends Application
{
#Override
public void start(Stage primaryStage)
{
List<ToggleButton> order = new ArrayList();//The buttons will be added to see ArrayList as they are pressed. ArrayList maintains order
ToggleButton btn4 = new ToggleButton();
btn4.setText("btn4");
btn4.setOnAction(actionEvent ->
{
btn4.setSelected(false);//When you selected a ToggleButton it stays pressed until it is pressed again. So we use code to unpress it.
order.add(btn4);
});
ToggleButton btn3 = new ToggleButton();
btn3.setText("btn3'");
btn3.setOnAction(actionEvent ->
{
btn3.setSelected(false);//When you selected a ToggleButton it stays pressed until it is pressed again. So we use code to unpress it.
order.add(btn3);
});
ToggleButton btn2 = new ToggleButton();
btn2.setText("btn2");
btn2.setOnAction(actionEvent ->
{
btn2.setSelected(false);//When you selected a ToggleButton it stays pressed until it is pressed again. So we use code to unpress it.
order.add(btn2);
});
ToggleButton btn1 = new ToggleButton();
btn1.setText("btn1");
btn1.setOnAction(actionEvent ->
{
btn1.setSelected(false);//When you selected a ToggleButton it stays pressed until it is pressed again. So we use code to unpress it.
order.add(btn1);
});
Button btnPlay = new Button("play");
btnPlay.setOnAction(actionEvent ->
{
List<Timeline> timeLines = new ArrayList();//This List will hold the TimeLines so that it can be used later to play the animation in order.
for (ToggleButton tempToggleButton : order)//Create a press animation and a release animation for each ToggleButton in the List order
{
KeyFrame pressButton = new KeyFrame(Duration.seconds(1),
(kfActionEvent) ->
{
System.out.println(tempToggleButton.getText() + "Selected!");
tempToggleButton.setSelected(true);
});
Timeline pressTimeline = new Timeline();
pressTimeline.getKeyFrames().addAll(pressButton);
timeLines.add(pressTimeline);
KeyFrame releaseButton = new KeyFrame(Duration.seconds(1),
(kfActionEvent) ->
{
System.out.println(tempToggleButton.getText() + "Unselected!");
tempToggleButton.setSelected(false);
});
Timeline releaseTimeline = new Timeline();
releaseTimeline.getKeyFrames().addAll(releaseButton);
timeLines.add(releaseTimeline);
}
SequentialTransition sequentialTransition = new SequentialTransition();
sequentialTransition.getChildren().addAll(timeLines);//Add all the Timelines created to a SequentialTransition
sequentialTransition.play();
sequentialTransition.setOnFinished(stActionEvent -> {
timeLines.clear();//Once done, clear the animations
});
});
VBox root = new VBox();
root.getChildren().addAll(btn1, btn2, btn3, btn4, btnPlay);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Related
I am creating a JavaFx dialog box and I have written to a large extent the code. My problem is how to display the error message if a user enters the invalid input. I know I have to use a while loop somewhere but not sure where because of the structure of JavaFx dialog box. Second problem is if the user enters the right input, say 1 for yes, I would want to call a function to carry out a task.
The code I have written brings up the pop up box and prints the consequence of the user input to the console.
public static void AnotherMatch() {
//creates a popUp window
Stage popUp = new Stage();
// makes sure no changes are made in the Main window while this window is open
popUp.initModality(Modality.APPLICATION_MODAL);
popUp.setTitle("New Game");
popUp.setMinWidth(400);
popUp.setHeight(200);
TextPanel textPanel2 = new TextPanel();
TextField nameInput = new TextField();
Button button = new Button("Enter");
//label explains how the game works
Label displayLabel = new Label();
displayLabel.setText("Do you want to play another match: Yes: 1 -- No: 2");
button.setOnAction(e -> isChoice(nameInput, nameInput.getText()));
//vbox stores label and is set in centre
VBox windowDisplay = new VBox();
windowDisplay.setStyle("-fx-background-color:Wheat"); //background colour is set
windowDisplay.getChildren().addAll(displayLabel,nameInput, button);
windowDisplay.setAlignment(Pos.CENTER);
Scene scene = new Scene(windowDisplay);
popUp.setScene(scene);
popUp.showAndWait(); }
Code for isChoice function
private static boolean isChoice(TextField nameInput, String message) {
// TODO Auto-generated method stub
try {
int choice = Integer.parseInt(nameInput.getText());
if(choice == 1) {
System.out.println("I want to play game again");
return true;
}
else if (choice == 2){
System.out.println("I want to stop playing");
return false;
}
else {
System.out.println("Invalid entry");
return false;
}
}
catch(NumberFormatException e){
System.out.println(message + " Invalid .Enter 1 for yes and 2 for no");
return false;
}
}
The user should be asked to enter yes or no. If the user invalid input, an error message should be displayed to the user and the answer asked again until they answer yes or no.
One way you can do is using Bindings to disable the Button unless the TextField contains Yes or No(ignore case).
Demo App using Bindings.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication357 extends Application
{
#Override
public void start(Stage primaryStage)
{
TextField textField = new TextField();
Button btn = new Button();
btn.disableProperty().bind(Bindings.notEqualIgnoreCase("yes", textField.textProperty()).and(Bindings.notEqualIgnoreCase("no", textField.textProperty())));
btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane(new VBox(textField, btn));
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
I am building an application which is an exam. All the questions are shown on different screens, all with a progressbar showing how much time the user has left.
When the progressbar is full, the screen with another question is shown and the progressbar and timeline should be resetted.
When the time is up, the next screen is shown and the timeline resets, but the progressbar (shown in code as 'PRGB') remains full and it will not reset to an empty progressbar. This is my code:
public void timeBar() throws Exception{
Timeline timeline = new Timeline(
new KeyFrame(Duration.ONE, new KeyValue(PRGB.progressProperty(), 0)),
new KeyFrame(Duration.seconds(Main.time), e-> {
}, new KeyValue(PRGB.progressProperty(), 1))
);
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event -> {
Main.setPane(questionNumber);
questionNumber++;
timeline.play();
//in here the progressbar has to be resetted
});
}
Update:
I have deleted my ProgressBar from SceneBuilder, and made a new one on which I then run the code below (with a few alterations taking by your suggestions). But now the progressbar does reset it self, but it does not show the progress: it stays empty.
public void timeBar() throws Exception{
Timeline timeline = new Timeline(
//I only use two keyframes instead of three
new KeyFrame(Duration.seconds(Main.time), e-> {
}, new KeyValue(PRGB.progressProperty(), 1))
);
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event -> {
Main.setPane(questionNumber);
questionNumber++;
//The setProgress is added
PRGB.setProgress(0.0F);
timeline.play();
});
}
This can probably be done better. In this example, 3 seconds is given for each question with 1 second at the end of each question to load the next question. Code from here.
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
Label lblCurrentQuestion = new Label();
ProgressBar prgb = new ProgressBar(0);
int numberOfQuestions = 5;//Number of questions
int timeForEachQuestion = 3;//3 second to do each questions
AtomicInteger currentProgressCounter = new AtomicInteger(0);
AtomicInteger currentQuestionCounter = new AtomicInteger(1);
Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), (event) -> {
if (currentProgressCounter.get() == timeForEachQuestion + 1) {
//Go to next question!
prgb.setProgress(0);
currentProgressCounter.set(0);
currentQuestionCounter.incrementAndGet();
}
lblCurrentQuestion.setText("Question " + currentQuestionCounter.get());
prgb.setProgress(currentProgressCounter.getAndIncrement() / (double) timeForEachQuestion);
}));
fiveSecondsWonder.setCycleCount(numberOfQuestions * (timeForEachQuestion + 1));
fiveSecondsWonder.play();
fiveSecondsWonder.setOnFinished(event -> {
PauseTransition wait = new PauseTransition(Duration.seconds(1));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
lblCurrentQuestion.setText("Quiz done!");
prgb.setProgress(0);
wait.playFromStart();
});
wait.play();
});
Scene scene = new Scene(new StackPane(new VBox(lblCurrentQuestion, prgb)), 500, 500);
stage.setScene(scene);
stage.show();
}
}
You have a call to timeline.play() in your setOnFinished() call. Remove that call so that the progress bar doesn't get reset to full.
Also, you should reset your progress bar by doing progressBar.setProgress(0.0F); in the setOnFinished()
I have a javafx application with multiple textboxes that the user can enter information in. I also have a keyboard built into the application that when pressed adds that text to the textbox.
My issue is that since I have multiple textboxes, I don't know which one to add the buttons text to. Is there a way in javafx to check if a user has clicked on a certain textbox so I can check which one has been selected and add the text there?
You can use the Scene.focusOwner property of the active scene to get the focused node. Check, if it's a TextInputControl and call the appropriate method for the button clicked. Note that clicking a button may move the focus, if focusTraversable is true for that button. (By default this is the case.)
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
final Scene scene = new Scene(grid);
for (int i = 0; i < 4; i++) {
grid.add(new TextField(), 0, i);
final String buttonValue = Character.toString((char) ('a'+i));
Button button = new Button(buttonValue);
button.setFocusTraversable(false); // prevent buttons from stealing focus
button.setOnAction(evt -> {
Node fo = scene.getFocusOwner();
if (fo instanceof TextInputControl) {
((TextInputControl) fo).replaceSelection(buttonValue);
}
});
grid.add(button, 1, i);
}
primaryStage.setScene(scene);
primaryStage.show();
}
You should create a listener for each TextField's focusProperty and set an instance variable.
Once you have a global reference to the currently focused TextField, you can do any processing on it that you choose.
Here is a quick application to demonstrate. I've included a couple extra details in the code itself:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
// Instance variable to hold the currently-selected TextField
private TextField selectedTextField;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Create TextFields
TextField txt1 = new TextField();
TextField txt2 = new TextField();
TextField txt3 = new TextField();
TextField txt4 = new TextField();
// This method sets the same change listener on each textfield
installListener(txt1, txt2, txt3, txt4);
VBox pane = new VBox(5);
pane.setPadding(new Insets(5));
// Add the TextFields to the layout
pane.getChildren().addAll(
new HBox(5, new Label("Txt1: "), txt1),
new HBox(5, new Label("Txt2: "), txt2),
new HBox(5, new Label("Txt3: "), txt3),
new HBox(5, new Label("Txt4: "), txt4)
);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
// Accepts multiple TextFields
private void installListener(TextField... textFields) {
// Install the same listener on all of them
for (TextField textField : textFields) {
textField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
// Set the selectedTextField to null whenever focus is lost. This accounts for the
// TextField losing focus to another control that is NOT a TextField
selectedTextField = null;
if (newValue) {
// The new textfield is focused, so set the global reference
selectedTextField = textField;
System.out.println("Selected Text: " + selectedTextField.getText());
}
});
}
}
}
I would like to be able to capture the MOUSEENTER event when hovering over a tab.
I have tried to do it from the Graphic of the tab, which is not the optimal solution, but it is a Node object with such events.
This is what I wrote:
tab.getGraphic().setOnMouseEntered((MouseEvent event) -> {
System.out.println("..... mouse entered");
//...
});
This solution does not error but is ignored by Javafx, any way to do this?
UPDATE: The way to create the tab and add its graphic, is like the following excerpt. The tab itself works fine and the graphic displays fine.
Tab tab = addChatTab(root, strName, strID, chat, false);
// setup tab graphic
switch (win.type) {
case wtChat:
if (chat !=null)
if (chat.isPublic()) {
tab.setGraphic(new ImageView(Main.me.imgTabPublic));
} else {
if (chat.isDCC())
tab.setGraphic(new ImageView(Main.me.imgTabDCC));
else tab.setGraphic(new ImageView(Main.me.imgTabPrivate));
}
break;
case wtWall:
tab.setGraphic(new ImageView(Main.me.imgTabWall));
break;
case wtMessage:
tab.setGraphic(new ImageView(Main.me.imgTabMessage));
break;
}
If you set a mouse handler on a graphic, then the handler will only be invoked when the mouse interacts with the graphic itself. In this example, the first tab has both text and a graphic set, so the mouse handler is not invoked when the mouse moves onto the text. The second tab sets no text but uses a label as the graphic, with the label containing the text. In that case the mouse handler is invoked when the mouse moves onto the text or image.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TabPaneHoverTest extends Application {
#Override
public void start(Stage primaryStage) {
// hover only applies on graphic:
Tab tab1 = new Tab("Tab 1");
tab1.setGraphic(new Rectangle(16, 16, Color.RED));
// Tab only uses graphic (no text),
// so hover appears to apply to whole tab:
Tab tab2 = new Tab();
Label tab2Graphic = new Label("Tab 2", new Rectangle(16, 16, Color.GREEN));
tab2.setGraphic(tab2Graphic);
tab1.getGraphic().setOnMouseEntered(e -> System.out.println("Hover on tab 1"));
tab2.getGraphic().setOnMouseEntered(e -> System.out.println("Hover on tab 2"));
BorderPane root = new BorderPane(new TabPane(tab1, tab2));
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a JavaFX ContextMenu assigned to the right mouse button click of a scrollpane. It opens, but it doesn't close when you click outside the scrollpane. I could add another mouse event to the scrollpane in order to hide it, but that solves only 1 problem. The main problem is that when I click on any component of the scrollpane, then the context menu remains open.
Example: Open popup via right mouse button click, then click on the button. The popup menu is still open.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final ContextMenu contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("About");
MenuItem item2 = new MenuItem("Preferences");
contextMenu.getItems().addAll(item1, item2);
Rectangle rect = new Rectangle( 100,100,150,150);
Button button = new Button( "Button Text");
// create nodes
Group root = new Group();
root.getChildren().add( rect);
root.getChildren().add( button);
// create scrollpane
ScrollPane sp = new ScrollPane( root);
sp.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isSecondaryButtonDown()) {
contextMenu.show( sp, event.getScreenX(), event.getScreenY());
}
}
});
// create scene
Scene scene = new Scene(sp, 400, 400, Color.WHITE);
// add scene to primary stage
primaryStage.setScene( scene);
primaryStage.show();
}
}
The documentation says that there's a setAutoHide method, but it doesn't work in my case:
Specifies whether Popups should auto hide. If a popup loses focus and
autoHide is true, then the popup will be hidden automatically. The
only exception is when owner Node is specified using
show(javafx.scene.Node, double, double). Focusing owner Node will not
hide the PopupWindow.
#defaultValue false
Thank you very much!
Interacting with child elements of the parent, will get a focus to that parent. So the context menu will not hide when the button in your code is clicked.
Try these two approaches:
1) Manually manage the visibility of context menu, i.e. hide it on button click:
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
contextMenu.hide();
}
});
2) Use setContextMenu() instead of showing the context menu on mouse press event:
sp.setContextMenu(contextMenu);
I know that this is old post, but for any newcomer I found a new solution. I have an jdk 1.8 and I have the same problem as you, but I have a dynamic generated context menu in TableView. So when you right click on the row I need another context menu by the row content. The key for my solution is that you execute show method in the context menu you pass on the window parameter to the method. Example of my code is below:
ContextMenu contextMenu = this.createContextMenu();
contextMenu.show(this.tableView.getScene().getWindow(), mouseEvent.getScreenX(), mouseEvent.getScreenY());
And when I click to another location of my program, the context menu hide.