javafx animation with PathTransition is not smooth - animation

I am learning javafx.animation. I created an ellipse and a rectangle and make rectangle travel along the border of ellipse . I make this animation repeat indefinitely. The problem is the transition between animation circles is not smooth. That means after the first round of animation finishes, it take a noticeable amount of time for next round of animation to start. This just looks ugly. Is there anyway to make it more smooth?
Thank you in advance.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Rectangle;
import javafx.animation.PathTransition;
import javafx.util.Duration;
import javafx.scene.paint.Color;
import javafx.animation.Timeline;
public class TestAnimation extends Application {
#Override
public void start(Stage stage){
//create pane and nodes
Pane pane = new Pane();
Ellipse ellipse = new Ellipse(300, 300, 100, 200);
ellipse.setFill(Color.BLACK);
Rectangle rect = new Rectangle(50, 80);
rect.setFill(Color.CRIMSON);
pane.getChildren().addAll(ellipse, rect);
//create animation
PathTransition path = new PathTransition(Duration.millis(2400), ellipse, rect);
path.setCycleCount(Timeline.INDEFINITE);
//path.setAutoReverse(true);
path.play();
//define mouse event
ellipse.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
path.pause();
}
});
ellipse.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
path.play();
}
});
//set up stage
Scene scene = new Scene(pane, 600, 600);
stage.setScene(scene);
stage.show();
}
}

Related

Set text color of JavaFX TextField without CSS

I've been working a lot in JavaFX lately, by writing code only -- no CSS no FXML and I want to keep it that way. I've managed to do all of the stuff I wanted that way except that now I can't set the text color of TextField without using CSS, so I'm asking for advice here. Is there any way to do it, no matter how hacky it is?
Note that I'm still using Java 8 version.
Considering your points : "No CSS" and "how hacky it can be.." below is one way to achieve the text coloring.
import com.sun.javafx.scene.control.skin.TextFieldSkin;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class TextField_TextColor_Demo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TextField textField = new TextField();
textField.setSkin(new TextFieldSkin(textField){
#Override
protected void layoutChildren(double x, double y, double w, double h) {
super.layoutChildren(x, y, w, h);
if(textField.getProperties().get("colorChanged")==null) {
textFill.setValue(Color.RED);
textField.getProperties().put("colorChanged",true);
}
}
});
StackPane root = new StackPane(textField);
Scene scene = new Scene(root, 300,300);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String... args){
Application.launch(args);
}
}
I could not make it work. But made another solution. Although the caret color also changes.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.skin.TextFieldSkin;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
public class TextField_TextColor_Demo extends Application {
#Override
public void start(Stage stage) throws Exception {
TextField txt = new TextField();
CustomTextFieldSkin skin = new CustomTextFieldSkin(txt);
txt.setSkin(skin);
skin.setHighlight(Color.RED);
HBox box = new HBox(txt);;
Scene scene = new Scene(box, 300, 100);
stage.setScene(scene);
stage.show();
}
public class CustomTextFieldSkin extends TextFieldSkin{
public CustomTextFieldSkin(TextField textField) {
super(textField);
}
public void setHighlight(Paint value) {
setTextFill(value);
}
}
public static void main(String... args){
Application.launch(args);
}
}
Seems it is possible to color the caret separately, according these two answers:
https://stackoverflow.com/a/47876289/7989121
https://stackoverflow.com/a/27323035/7989121
Both use caretPath though, which I could not use due it not being visible according to my IDE

Event handler for objects of the same Pane

i'm studying the mechanism of the event handler on javaFX ,but i'm not sure that i have understand it , in Fact I have a little doubt :
if i have two object , they have all necessary code to handle the event (EventHandler interface ecc..),that they BELONG at the SAME stackPane ,the question is : is there a way for the 1st object to launch an event (ActionEvent for example ) that will be handled by the 2 object though they belong at the same Pane?
Because for what i have understand about the "event route",this is not possible ,at least directly.
In essence my little program have a splitpane that divided in two stackpane the screen,in the left panel i have put a gridpane with the button, each of them have the function that permit to draw a different shape ,in the right panel with a canvas.
My idea it was to launch an ActionEvent in setonaction of each button ,implements the EventHandlers on canvas to capture the event
with relative handle method ,and in the corp of he handle mode discriminate which button is Clicked for drawing the correct shape.
Can someone help me ? Thanks a lot anyway
package es1;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
*
* #author DAVIDE
*/
public class Es1 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
lanciaevento(this.getClass().toString());
}
});
//add a button on left panel and a textfiled on the right for test if the event launched
//on the click of the button is reached by the textfield
Textfield text = new Textfield();
StackPane panel1 = new StackPane();
panel1.getChildren().addAll(btn);
StackPane panel2 = new StackPane();
panel2.getChildren().addAll(text);
splitpane divisore = new splitpane();
divisore.addEventHandler(ActionEvent.ACTION, divisore);
divisore.getItems().addAll(panel1,panel2);
Scene scene = new Scene(divisore, 600, 450);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public void lanciaevento(String oggetto)
{
ActionEvent evento = new ActionEvent();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
package es1;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.SplitPane;
/**
*
* #author DAVIDE
*/
public class splitpane extends SplitPane implements EventHandler<ActionEvent>{
private String message_event;
public String get_message()
{
return(message_event);
}
public void set_message(String messaggio)
{
message_event = messaggio;
}
#Override
public void handle(ActionEvent event) {
System.out.println("mi ha mandato un messaggio "+event.getSource().toString());
}
}
/*
* To change this license header, choose License Headers in Project Properties.
package es1;
import javafx.event.ActionEvent;
javafx.event.EventHandler;
import javafx.scene.control.TextField;
/**
*
* #author DAVIDE
*/
public class Textfield extends TextField implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
this.appendText(event.getSource().toString());
}
}

Slide in/out JavaFX stage from/to right side of the screen

I have a task to make JavaFX application. When I start it the window should slides smoothly from right side of screen and when I click on "x" button it's should slides out to right side and then finishes.
I found possible to use simple Timeline animation for this. I made the window to slide in, when I run app, but can't figure out how to slide out window.
I tried to handle this defining handler via setOnCloseRequest() method of stage, but stuck with two problems:
can't implement animation
after click on "x" application closed immediately even if I use consume() method of Window event
Code:
public class Main extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Main");
Group root = new Group();
Scene scene = new Scene(root, 300, 250);
stage.setScene(scene);
Rectangle2D primScreenBounds = Screen.getPrimary().getVisualBounds();
stage.setX(primScreenBounds.getMinX() + primScreenBounds.getWidth());
System.out.println(primScreenBounds.getWidth());
stage.setY(primScreenBounds.getMinY());
stage.setWidth(0);
stage.setHeight(primScreenBounds.getHeight());
Timeline timeline = new Timeline();
timeline.setAutoReverse(true);
WritableValue<Double> writableWidth = new WritableValue<Double>() {
#Override
public Double getValue() {
return stage.getWidth();
}
#Override
public void setValue(Double value) {
stage.setWidth(value);
}
};
KeyValue kv = new KeyValue(writableWidth, 600d);
KeyFrame kf = new KeyFrame(Duration.millis(3000), kv);
timeline.getKeyFrames().addAll(kf);
timeline.play();
stage.show();
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
event.consume();
}
});
}
}
This works (kind of, it is not very smooth) for me:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.WritableValue;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
public class SlidingWindow extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Main");
Group root = new Group();
Scene scene = new Scene(root);
stage.setScene(scene);
Rectangle2D primScreenBounds = Screen.getPrimary().getVisualBounds();
double screenRightEdge = primScreenBounds.getMaxX() ;
stage.setX(screenRightEdge);
System.out.println(primScreenBounds.getWidth());
stage.setY(primScreenBounds.getMinY());
stage.setWidth(0);
stage.setHeight(primScreenBounds.getHeight());
Timeline timeline = new Timeline();
WritableValue<Double> writableWidth = new WritableValue<Double>() {
#Override
public Double getValue() {
return stage.getWidth();
}
#Override
public void setValue(Double value) {
stage.setX(screenRightEdge - value);
stage.setWidth(value);
}
};
KeyValue kv = new KeyValue(writableWidth, 600d);
KeyFrame kf = new KeyFrame(Duration.millis(3000), kv);
timeline.getKeyFrames().addAll(kf);
timeline.play();
stage.show();
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Timeline timeline = new Timeline();
KeyFrame endFrame = new KeyFrame(Duration.millis(3000), new KeyValue(writableWidth, 0.0));
timeline.getKeyFrames().add(endFrame);
timeline.setOnFinished(e -> Platform.runLater(() -> stage.hide()));
timeline.play();
event.consume();
}
});
}
}
The Platform.runLater(...) seems necessary to prevent a slew of NullPointerExceptions when the window is hidden, probably because the animation is causing some system to try to access a stage that no longer exists.

javafx how to separate events of a StackPane and events of an element contained?

I have a StackPane containing several elements (Rectangles). I added an ActionEventHandler to all of them. I expect that when Rectangle object is clicked it starts anly the event added to its. Instead I see that both events are started, like I have pressed Rectangle and StackPane together. Here's the code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package provapanetile;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author Ambra
*/
public class ProvaPaneTile extends Application {
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
TilePane tp = new TilePane();
tp.setVgap(10);
tp.setHgap(10);
for(int i = 0; i<5; i++){
tp.getChildren().add(new Rectangle(50, 50, Color.AZURE));
}
addListners(root, tp);
root.getChildren().add(tp);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public void addListners(StackPane sp, TilePane tp){
sp.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("You pressed StackPointer");
}
});
tp.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("You pressed TilePane");
}
});
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Clicking ones on a Rectangle I get this 2 messagges:
You pressed TilePane
You pressed StackPointer
Consume the event in the handler on the tile pane:
tp.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("You pressed TilePane");
event.consume();
}
});
Now when you click on the TilePane, only the handler registered with the tile pane will be invoked.
Note that in this example, the TilePane entirely covers the StackPane, so it's impossible to actually invoke the handler on the StackPane, which is probably not what you want. To fix that, you would need to manage the layout so that the TilePane did not grow to the full size of the StackPane; but obviously this is just an example to demonstrate the issue so perhaps this is enough for your real application...

Add image to a button at a specific position JavaFX

When I add image and text to a button, by default elements are set horizontally. How can I change this behavior to get text under image ?
Set the contentDisplayProperty on the button.
button.setContentDisplay(ContentDisplay.TOP);
Here is an executable example:
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ButtonGraphicTest extends Application {
#Override public void start(final Stage stage) throws Exception {
final Label response = new Label();
final ImageView imageView = new ImageView(
new Image("http://icons.iconarchive.com/icons/eponas-deeway/colobrush/128/heart-2-icon.png")
);
final Button button = new Button("I love you", imageView);
button.setStyle("-fx-base: coral;");
button.setContentDisplay(ContentDisplay.TOP);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
response.setText("I love you too!");
}
});
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.getChildren().addAll(button, response);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
// icon license: (creative commons with attribution) http://creativecommons.org/licenses/by-nc-nd/3.0/
// icon artist attribution page: (eponas-deeway) http://eponas-deeway.deviantart.com/gallery/#/d1s7uih

Resources