英文:
JavaFx how to keep animation running even if next animation is coming in?
问题
I have created an animation where prompt text will move up as text to the top of its text field when the field is focused, and move downwards when out of focus. However, if the user clicks too fast ( like keep pressing 'tab'), then the animation will the position of the prompt text will go chaos. How do i fix it?
我已经创建了一个动画,其中提示文本在字段获得焦点时会向上移动,移到文本字段的顶部,而在失去焦点时则向下移动。然而,如果用户点击得太快(比如不断按 'tab' 键),那么动画会使提示文本的位置混乱。如何修复它?
英文:
I have created an animation where prompt text will move up as text to the top of its text field when the field is focused, and move downwards when out of focus. However, if the user clicks too fast ( like keep pressing 'tab'), then the animation will the position of the prompt text will go chaos. How do i fix it?
import javafx.animation.TranslateTransition;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
public class Sing_Up implements Initializable {
@FXML
private TextField textField1;
@FXML
private TextField textField2;
@FXML
private TextField textField3;
@FXML
private TextField textField4;
@FXML
GridPane d;
@FXML
Button button;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
button.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Home.fxml"));
try {
button.getScene().setRoot(loader.load());
} catch (IOException e) {
e.printStackTrace();
}
});
for (Node node : d.getChildren()) {
System.out.println(node);
// check if the node is a TextField
if (node instanceof TextField) {
TextField textField = (TextField) node;
// apply the animate() method to the TextField
animate(textField);
}
}}
public void animate(TextField textField){
Text promptText;
String p=textField.getPromptText();
// Create a new Text node to represent the prompt text
promptText = new Text(textField.getPromptText());
promptText.setFill(Color.RED);
promptText.setFont(textField.getFont());
GridPane parent = (GridPane) textField.getParent();
GridPane.getRowIndex(textField1);
// Animate the prompt text when the TextField receives focus
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
// Position the prompt text just above the TextField
if(!parent.getChildren().contains(promptText)&& !(GridPane.getRowIndex(textField) ==null)){
parent.add(promptText,0, GridPane.getRowIndex(textField));}
promptText.setVisible(true);
if (newValue&&textField.getText().equals("")) {
promptText.setVisible(true);
TranslateTransition tt = new TranslateTransition(Duration.seconds(0.3), promptText);
tt.setByY(-35);
tt.play();
} else {
if (!newValue && textField.getText().isEmpty()){
TranslateTransition tt = new TranslateTransition(Duration.seconds(0.3), promptText);
tt.setByY (35);
tt.play();
tt.setOnFinished(event -> {
promptText.setVisible(false);
});};
;
}
;});}}
答案1
得分: 4
以下是您要翻译的内容:
"While you can consider all the other suggestions in the comments, below is my version of addressing this issue.
Personally I would prefer to create a custom control that can handle this automatically irrespective of its Parent. Currently your logic seems to assume that the TextField is always in a GridPane.
I am considering the usage of Timeline
rather than TranslateTransition
, because of the fact that I only need the 'end value' for using Timeline. But with TranslateTransition
, you need to provide the 'by' value or the 'from/to' values to run the transition (which is the reason it is getting messed up in your case).
Below is the demo of the custom TextField that demonstrates the behavior when rendered in different Parent
nodes (here VBox & GridPane)
If you want to show the promptText animation conditionally, change the logic of the isPromptNeeded()
method."
接下来是代码部分,不会进行翻译。
英文:
While you can consider all the other suggestions in the comments, below is my version of addressing this issue.
Personally I would prefer to create a custom control that can handle this automatically irrespective of its Parent. Currently your logic seems to assume that the TextField is always in a GridPane.
I am considering the usage of Timeline
rather than TranslateTransition
, because of the fact that I only need the 'end value' for using Timeline. But with TranslateTransition
, you need to provide the 'by' value or the 'from/to' values to run the transition (which is the reason it is getting messed up in your case).
Below is the demo of the custom TextField that demonstrates the behavior when rendered in different Parent
nodes (here VBox & GridPane)
If you want to show the promptText animation conditionally, change the logic of the isPromptNeeded()
method.
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PromptTextFieldDemo extends Application {
@Override
public void start(final Stage stage) throws Exception {
VBox root = new VBox();
root.setPadding(new Insets(20));
root.setSpacing(20);
/* Add some fields in the GridPane */
GridPane detailsGrid = new GridPane();
detailsGrid.setHgap(20);
detailsGrid.setVgap(20);
detailsGrid.addRow(0, getField("First Name"), getField("Last Name"), getField("Age"));
detailsGrid.addRow(1, getField("City"), getField("Company"));
/* Add some fields in the VBox */
root.getChildren().addAll(detailsGrid, getField("Address 1"), getField("Address 2"), getField("Address 3"));
Scene scene = new Scene(root, 450, 300);
stage.setScene(scene);
stage.setTitle("PromptTextField Demo");
stage.show();
}
private PromptTextField getField(String text) {
PromptTextField textField = new PromptTextField();
textField.setPromptText(text);
return textField;
}
class PromptTextField extends TextField {
private Text promptNode;
private Timeline show;
private Timeline hide;
private boolean alwaysPrompt = true;
public PromptTextField() {
super();
}
public PromptTextField(String text) {
super(text);
}
public void setAlwaysPrompt(final boolean alwaysPrompt) {
this.alwaysPrompt = alwaysPrompt;
}
@Override
protected void layoutChildren() {
super.layoutChildren();
if (getPromptText() != null && !getPromptText().isEmpty() && !getChildren().contains(getPromptNode())) {
getPromptNode().setText(getPromptText());
getPromptNode().setFont(getFont());
getChildren().add(getPromptNode());
}
}
public Text getPromptNode() {
if (promptNode == null) {
promptNode = new Text();
promptNode.setFill(Color.RED);
promptNode.setFont(getFont());
promptNode.setVisible(false);
/* Turn off the managed property, so that you control its position using layoutX/Y only. */
promptNode.setManaged(false);
promptNode.setLayoutX(8);
promptNode.setLayoutY(17);
focusedProperty().addListener((obs, old, focused) -> {
if (getPromptText() != null && !getPromptText().isEmpty()) {
if (focused && isPromptNeeded()) {
if (hide.getStatus() == Animation.Status.RUNNING) {
hide.stop();
}
promptNode.setVisible(true);
show.play();
} else if(!focused){
if (show.getStatus() == Animation.Status.RUNNING) {
show.stop();
}
hide.play();
}
}
});
final KeyFrame showKeyFrame = new KeyFrame(Duration.millis(300), new KeyValue(promptNode.layoutYProperty(), -5));
show = new Timeline(showKeyFrame);
final KeyFrame hideKeyFrame = new KeyFrame(Duration.millis(300), new KeyValue(promptNode.layoutYProperty(), 17));
hide = new Timeline(hideKeyFrame);
hide.setOnFinished(e -> promptNode.setVisible(false));
}
return promptNode;
}
private boolean isPromptNeeded(){
/* Add your custom logic here: about when to show the prompt text animation */
return alwaysPrompt || getText()==null || getText().isEmpty();
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论