what's the matter with this code?
i'm relly confused!!
i wanted to change my scene in main stage.
public class SignInController {
#FXML
TextField SignInPassword;
#FXML
TextField SignInUsername;
#FXML
CheckBox RememberMe;
public void signUpScene(MouseEvent mouseEvent) throws IOException {
Timeline timeline = new Timeline();
Scene SignUpScene = new Scene(FXMLLoader.load(getClass().getResource("sign up.fxml")),700,700);
Main.pstage.setScene(SignUpScene);
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.ZERO,new KeyValue(SignUpScene.getWidth(),0.0 )),
new KeyFrame(Duration.millis(1000.0d),new KeyValue(SignUpScene.getWidth(),700.0 ) )
);
timeline.play();
}
}
If you want to animate the width of the stage holding your new scene, you can use a Transition:
public void signUpScene(MouseEvent mouseEvent) throws IOException {
Scene SignUpScene = new Scene(FXMLLoader.load(getClass().getResource("sign up.fxml")),700,700);
Main.pstage.setScene(SignUpScene);
Rectangle clip = new Rectangle(0, 700);
Transition animateStage = new Transition() {
{
setCycleDuration(Duration.millis(1000));
}
#Override
protected void interpolate(double t) {
Main.pstage.setWidth(t * 700.0);
}
};
animateStage.play();
}
}
Maybe a better approach would be to gradually reveal the new scene using a clip:
public void signUpScene(MouseEvent mouseEvent) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("sign up.fxml"));
Scene SignUpScene = new Scene(root,700,700);
Main.pstage.setScene(SignUpScene);
Rectangle clip = new Rectangle(0, 700);
Timeline animate = new Timeline(
new KeyFrame(Duration.millis(1000),
new KeyValue(clip.widthProperty(), 700.0));
root.setClip(clip);
// when animation finishes, remove clip:
animate.setOnFinished(e -> root.setClip(null));
animate.play();
}
}
Related
I want to make a notification window with animated text. A notification would be sent by a button click and the animation would start playing. My problem is that when I click the button again before the previous animation is done, two animations get executed at once. How do I make each method call of "sendMessage()" wait for the other to finish? If it has any significance there are multiple nodes that call the sendMessage() method in my program unlike in my MRE, so I want some kind of Queue with messages. Here is my MRE:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
#Override
public void start(Stage stage) throws IOException {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> sendMessage("Some animated text."));
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private void sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
public static void main(String[] args) {
launch();
}
}
For the specific example you posted, the easiest approach is to disable the button immediately prior to starting the animation, and enable it again when the animation stops. Here is one way to do this:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> {
Animation animation = sendMessage("Some animated text.");
button.disableProperty().bind(Bindings.equal(animation.statusProperty(), Animation.Status.RUNNING));
});
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private Animation sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
return timeline ;
}
public static void main(String[] args) {
launch();
}
}
If you want to allow these messages to accumulate in a queue, and a new animation to start when the old one finishes, you need to keep a queue of the messages and a reference to a current animation that's running (if there is one). You can poll the queue from an AnimationTimer and start a new animation when a new message appears, if there is no current animation running.
I'd recommend thinking about whether this is the approach you want to take; there's no guarantee here that your messages will not appear more quickly than they can be animated, in which case the queue will grow indefinitely. However, this is an implementation if you can otherwise assure that this is not the case:
public class AnimationTest extends Application {
private final Label messageLabel = new Label();
private final Queue<String> messages = new LinkedList<>();
private Animation currentAnimation = null ;
#Override
public void start(Stage stage) {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox, 320, 240);
vBox.getChildren().add(messageLabel);
Button button = new Button();
button.setOnAction(event -> messages.add("Some animated text."));
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long l) {
if (currentAnimation == null || currentAnimation.getStatus() == Animation.Status.STOPPED) {
String message = messages.poll();
if (message != null) {
currentAnimation = sendMessage(message);
currentAnimation.play();
}
}
}
};
timer.start();
vBox.getChildren().add(button);
stage.setScene(scene);
stage.show();
}
private Animation sendMessage(String message) {
final IntegerProperty i = new SimpleIntegerProperty(0);
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
Duration.millis(40),
event -> {
if (i.get() > message.length()) {
timeline.stop();
} else {
messageLabel.setText(message.substring(0, i.get()));
i.set(i.get() + 1);
}
});
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE);
return timeline ;
}
public static void main(String[] args) {
launch();
}
}
Note there are no threading considerations here. The handle() method is invoked on the FX Application Thread, so the only requirement is that the messages are placed in the queue on the same thread. This happens in this example because the button's event handler is invoked on that thread. If your messages are coming from a background thread, you should ensure they are added to the queue on the FX Application Thread, either by using Platform.runLater(...) or (preferably) by using the JavaFX Concurrency API (i.e. by retrieving the messages in a Task or Service and adding them to the queue in an onSucceeded handler).
Set a boolean (best do it atomic, because multithreading and stuff...) at animation start & end.
Disable the Button when boolean is in animation phase.
#FXML
void minimize(MouseEvent event) {
Stage stage=(Stage) iconMinimize.getScene().getWindow();
stage.setIconified(true);
}
I have an icon that minimizes my program by mouse click. For example, when I minimize Windows for a program, you can see how the program works with an effect. The program slowly moves back to the taskbar. I would like to have such an effect too. If I do that with the code from the top, the program is right in the system tray. How do I get such an effect?
Animate the window size when you want to iconify the app and listen to the iconified property to do the reverse animation when the Stage is restored:
#Override
public void start(Stage primaryStage) {
StageHideAnimator.create(primaryStage);
Button minimize = new Button("minimize");
minimize.setOnAction(evt -> {
StageHideAnimator animator = StageHideAnimator.getStageHideAnimator((Node) evt.getSource());
animator.iconify();
});
Button close = new Button("close");
close.setOnAction(evt -> primaryStage.close());
VBox content = new VBox(minimize, close, new Rectangle(200, 200, Color.BLUE));
content.setPadding(new Insets(10));
content.setStyle("-fx-background-color: green;");
primaryStage.initStyle(StageStyle.TRANSPARENT);
Scene scene = new Scene(content);
primaryStage.setScene(scene);
primaryStage.setOnShown(evt -> {
WindowUtils.placeAtPrimaryScreenBottom(primaryStage);
});
primaryStage.show();
}
public final class WindowUtils {
private WindowUtils() { }
public static void placeAtPrimaryScreenBottom(Stage stage) {
stage.setY(Screen.getPrimary().getVisualBounds().getMaxY() - stage.getHeight());
}
}
public class StageHideAnimator {
// key used for storing animators in the properties map of a Stage
private static final Object PROPERTY_KEY = new Object();
private double sceneHeight;
private double decorationHeight;
private final Stage stage;
private Timeline animation;
// fraction of height relative to full height
private final DoubleProperty height = new SimpleDoubleProperty();
// getter for the animator
public static StageHideAnimator getStageHideAnimator(Stage stage) {
return (StageHideAnimator) stage.getProperties().get(PROPERTY_KEY);
}
// get animator of window containing the node
public static StageHideAnimator getStageHideAnimator(Node node) {
return getStageHideAnimator((Stage) node.getScene().getWindow());
}
private StageHideAnimator(Stage stage) {
this.stage = stage;
stage.iconifiedProperty().addListener((o, oldValue, newValue) -> {
// do reverse hide animation when stage is shown
if (!newValue) {
animation.setRate(-1);
if (animation.getStatus() == Animation.Status.STOPPED) {
animation.playFrom("end");
} else {
animation.play();
}
}
});
height.addListener((o, oldValue, newValue) -> {
// resize stage and put it at the bottom of the primary screen
stage.setHeight(sceneHeight * newValue.doubleValue() + decorationHeight);
WindowUtils.placeAtPrimaryScreenBottom(stage);
});
}
public static StageHideAnimator create(Stage stage) {
if (stage.getProperties().containsKey(PROPERTY_KEY)) {
// don't allow 2 animators
throw new IllegalArgumentException("animator already exists");
}
StageHideAnimator animator = new StageHideAnimator(stage);
stage.getProperties().put(PROPERTY_KEY, animator);
return animator;
}
private void initHeight() {
sceneHeight = stage.getScene().getHeight();
decorationHeight = stage.getHeight() - sceneHeight;
}
public void iconify() {
if (stage.isIconified()) {
return;
}
if (animation == null) {
initHeight(); // save initial height of stage
animation = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(height, 1d, Interpolator.EASE_BOTH)),
new KeyFrame(Duration.seconds(1), new KeyValue(height, 0d, Interpolator.EASE_BOTH)));
animation.setOnFinished(evt -> {
if (animation.getRate() == 1) {
// iconify at end of hiding animation
animation.setRate(-1);
stage.setIconified(true);
}
});
animation.play();
} else {
animation.setRate(1);
if (animation.getStatus() == Animation.Status.STOPPED) {
initHeight(); // save initial height of stage
animation.playFromStart();
} else {
animation.play();
}
}
}
}
I've been trying to make my application to switch between scenes. Here is a copy of part of the code. The credits scene simply has a back button which should return me to the main scene.
When I try to click on credits button on main scene it is becoming white a white screen. I believe that there is a better way to solve this problem could you give me some advices ?
public class Application {
public static void main(String[] args) {
javafx.application.Application.launch(GUI.class);
}
}
public class GUI extends Application {
#Override
public void start(Stage primaryStage) {
Scene mainScene, creditsScene = null;
mainScene = getMainScene(primaryStage, creditsScene);
creditsScene = getCreditsScene(primaryStage, mainScene);
primaryStage.setTitle("Test application");
primaryStage.setScene(mainScene);
primaryStage.show();
}
private Scene getMainScene(Stage primaryStage, Scene creditsScene) {
final Button credits = new Button("Credits");
credits.setOnAction((ActionEvent e) -> {
primaryStage.close();
primaryStage.setScene(creditsScene);
primaryStage.show();
});
VBox x = new VBox(50);
x.setAlignment(Pos.CENTER);
x.getChildren().addAll( run, displayInfo,
label1, displayInfo, textField, submitName, credits, exit);
//scene size
Scene scene = new Scene(x, 650, 900);
return scene;
}
private Scene getCreditsScene(Stage primaryStage, Scene main) {
final Button back = new Button("Back");
back.setOnAction((ActionEvent e) -> {
primaryStage.setScene(main);
});
VBox x = new VBox(50);
x.getChildren().addAll(back);
Scene credits = new Scene(x, 650, 900);
return credits;
}
Try to switch order of strings:
mainScene = getMainScene(primaryStage, creditsScene);
creditsScene = getCreditsScene(primaryStage, mainScene);
here you pass to getMainScene null.
I am building a javaFX application.
The example program which is online working well: http://www.java2s.com/Tutorials/Java/JavaFX/1010__JavaFX_Timeline_Animation.htm
I changed it a bit:
I want to have these nice moving circles in the background. That's why I made these tricks:
Main.java:
#Override
public void start(Stage primaryStage) {
try {
// load the FXML resource
FXMLLoader loader = new FXMLLoader(getClass().getResource("Dashboard.fxml"));
// store the root element so that the controllers can use it
StackPane root = new StackPane();
Pane bgRoot = new Pane();
GridPane userRoot = (GridPane) loader.load();
// create and style a scene
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
BackgroundScene bgScene = new BackgroundScene(root, bgRoot, primaryScreenBounds.getWidth(),
primaryScreenBounds.getHeight(), Color.BLACK);
// TODO CSS load:
// bgScene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
// create the stage with the given title and the previously created
// scene
primaryStage.setFullScreen(true);
primaryStage.setMaximized(true);
primaryStage.setResizable(false);
primaryStage.setMinWidth(primaryScreenBounds.getWidth());
primaryStage.setMinHeight(primaryScreenBounds.getHeight());
primaryStage.setScene(bgScene);
root.getChildren().add(bgRoot);
root.getChildren().add(userRoot);
// show the GUI
primaryStage.show();
userRoot.setPrefHeight(root.getHeight());
userRoot.setPrefWidth(root.getWidth());
} catch (Exception e) {
e.printStackTrace();
}
}
In the above code:
I have a stage with a stack pane.
There is a Pane on the StackPane with the Circles
On that there is a GridPane for controllers.
I made my own Scene as BackgroundScene:
public class BackgroundScene extends Scene {
private Pane bgRoot;
public BackgroundScene(Parent root, Pane bgRoot, double width, double height, Color color) {
super(root, width, height, color);
this.bgRoot = bgRoot;
makeGreenCircleAnimation();
}
public void makeGreenCircleAnimation() {
Group circles = new Group();
for (int i = 0; i < 30; i++) {
Circle circle = new Circle(Math.random() * this.getWidth(), Math.random() * this.getHeight(),
150, Color.web("white", 0.05));
circle.setStrokeType(StrokeType.OUTSIDE);
circle.setStroke(Color.web("white", 0.16));
circle.setStrokeWidth(4);
circles.getChildren().add(circle);
}
circles.setEffect(new BoxBlur(10, 10, 3));
Rectangle colors = new Rectangle(this.getWidth(), this.getHeight(),
new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE,
new Stop[] { new Stop(0, Color.web("#00ff00")), new Stop(0.14, Color.web("#11ee11")),
new Stop(0.28, Color.web("#22dd22")), new Stop(0.43, Color.web("#33cc33")),
new Stop(0.57, Color.web("#44bb44")), new Stop(0.71, Color.web("#55aa55")),
new Stop(0.85, Color.web("#669966")), new Stop(1, Color.web("#778877")), }));
Group blendModeGroup = new Group(
new Group(new Rectangle(this.getWidth(), this.getHeight(), Color.DARKGREEN), circles), colors);
colors.setBlendMode(BlendMode.OVERLAY);
bgRoot.getChildren().add(blendModeGroup);
Timeline timeline = new Timeline();
makeAnimation(circles, timeline);
timeline.play();
timeline.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
makeAnimation(circles, timeline);
timeline.play();
}
});
}
void makeAnimation(Group group, Timeline timeline) {
timeline.getKeyFrames().clear();
for (Node node : group.getChildren()) {
timeline.getKeyFrames()
.addAll(new KeyFrame(Duration.ZERO, // set start
// position at 0
new KeyValue(node.translateXProperty(), node.getLayoutX() + node.getTranslateX()),
new KeyValue(node.translateYProperty(), node.getLayoutY() + node.getTranslateY())),
new KeyFrame(new Duration(4000), // set end position at
// 40s
new KeyValue(node.translateXProperty(), Math.random() * this.getWidth()),
new KeyValue(node.translateYProperty(), Math.random() * this.getHeight())));
}
}
}
It contains the code from the tutorial (with minor changes).
I add the circles and everything to the Pane not to a Group (as in
the online example)
The problem is that it is seams to be moving:
The circles are moving to the right (always)
If I change something in the code:
Main.java (instantiating my scene):
BackgroundScene bgScene = new BackgroundScene(root, root, primaryScreenBounds.getWidth(),
primaryScreenBounds.getHeight(), Color.BLACK);
(so giving the circles to the StackPane)
This happens:
You can download the eclipse project:
https://www.dropbox.com/s/dovae0hxdtsnsdo/Test.7z?dl=0
Do you know what is the problem?
Or can you suggest something how I can put the circles nicely behind the button?
I've wrapped my brain around a challenge for 2 days now. I am all empty for ideas, so I hope someone out there know how to do this.
I got inspired by Angela Caicedo's city app, from the website https://blogs.oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx, and trying to make a similar app-gui to show available rooms and lecture halls at my University.
I am using Java FX to build the gui, and I get the whole GUI printed out, which is a java fx pane with a image on it. What I want, however, is to just see a small part of the gui (the backgroundimage I am using is w:1500px h:500, so each part will be w:500px h:500px), then be able to push a button or a arrow (or similar) to move the window to the next step. On top of the image there is 3 panes with w:500px h:500px snapped to each other. Maybe this is a bad solution, considering all the pane-types Java FX has available.
So, what I need is a constrained viewer of sorts.
I've also used FMXL to build the GUI, having one FMXL document, one Controller and a css-file to handle the design.
I'm sure I've been everywhere on the internet by now, so I really hope someone has done this before in Java FX :)
Ok, here is some code example. The first sample works nice, but I want to implement the second example instead. I am reading on the TranslateTransition of JavaFX, but my efforts of trying to switch the code is hopeless..
1'st example (working, and is fading in and out of the fxml screen):
public boolean setScreen(final String name){
if (screens.get(name) != null) { //screen loaded
final DoubleProperty opacity = opacityProperty();
if (!getChildren().isEmpty()) { //if there is more than one screen
Timeline fade = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1.0)),
new KeyFrame(new Duration(2000), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
getChildren().remove(0); //remove the displayed screen
getChildren().add(0, screens.get(name)); //add the screen
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(2000), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
}, new KeyValue(opacity, 0.0)));
fade.play();
} else {
setOpacity(0.0);
getChildren().add(screens.get(name)); //no one else been displayed, then just show
Timeline fadeIn = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 0.0)),
new KeyFrame(new Duration(1000), new KeyValue(opacity, 1.0)));
fadeIn.play();
}
return true;
} else {
System.out.println("screen hasn't been loaded!!! \n");
return false;
}
}
Second example, the TranslateTransition I want to implement instead:
private final double IMG_WIDTH = 500;
private final double IMG_HEIGHT = 500;
private final int NUM_OF_IMGS = 3;
private final int SLIDE_FREQ = 4; // in secs
#Override
public void start(Stage stage) throws Exception {
stage.initStyle(StageStyle.UNDECORATED);
StackPane root = new StackPane();
Pane clipPane = new Pane();
// To center the slide show incase maximized
clipPane.setMaxSize(IMG_WIDTH, IMG_HEIGHT);
clipPane.setClip(new Rectangle(IMG_WIDTH, IMG_HEIGHT));
HBox imgContainer = new HBox();
ImageView imgGreen = new ImageView(new Image(getClass().getResourceAsStream("uib_01.jpg")));
ImageView imgBlue = new ImageView(new Image(getClass().getResourceAsStream("uib_02.jpg")));
ImageView imgRose = new ImageView(new Image(getClass().getResourceAsStream("uib_03.jpg")));
imgContainer.getChildren().addAll(imgGreen, imgBlue, imgRose);
clipPane.getChildren().add(imgContainer);
root.getChildren().add(clipPane);
Scene scene = new Scene(root, IMG_WIDTH, IMG_HEIGHT);
stage.setTitle("Image Slider");
stage.setScene(scene);
startAnimation(imgContainer);
stage.show();
}
private void startAnimation(final HBox hbox) {
EventHandler<ActionEvent> slideAction = (ActionEvent t) -> {
TranslateTransition trans = new TranslateTransition(Duration.seconds(1.5), hbox);
trans.setByX(-IMG_WIDTH);
trans.setInterpolator(Interpolator.EASE_BOTH);
trans.play();
};
EventHandler<ActionEvent> resetAction = (ActionEvent t) -> {
TranslateTransition trans = new TranslateTransition(Duration.seconds(1), hbox);
trans.setByX((NUM_OF_IMGS - 1) * IMG_WIDTH);
trans.setInterpolator(Interpolator.EASE_BOTH);
trans.play();
};
List<KeyFrame> keyFrames = new ArrayList<>();
for (int i = 1; i <= NUM_OF_IMGS; i++) {
if (i == NUM_OF_IMGS) {
keyFrames.add(new KeyFrame(Duration.seconds(i * SLIDE_FREQ), resetAction));
} else {
keyFrames.add(new KeyFrame(Duration.seconds(i * SLIDE_FREQ), slideAction));
}
}
Timeline anim = new Timeline(keyFrames.toArray(new KeyFrame[NUM_OF_IMGS]));
anim.setCycleCount(Timeline.INDEFINITE);
anim.playFromStart();
}
The screen should change on button click. I have this in a separate controller class:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
public class roomAppController implements Initializable, ScreenController {
private ScreenPane myScreenPane;
#FXML
public ImageView bldArw_1;
public ImageView rmArw_1;
#FXML
private void handleExitButtonEvent(MouseEvent e) {
System.out.println("Button is clicked");
System.exit(0);
}
#FXML
private void handleNextPageEvent(MouseEvent e) {
if((ImageView)e.getSource() == bldArw_1) {
myScreenPane.setScreen("buildingScreen");
}
if((ImageView)e.getSource() == rmArw_1) {
myScreenPane.setScreen("roomScreen");
}
System.out.println("Clicked");
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
#Override
public void setScreenPane(ScreenPane screenPage) {
myScreenPane = screenPage;
}
}