JavaFX 字幕动画

标签 java animation javafx

您可能见过一些应用程序,当选项卡中的String无法完全显示时,它会以动画形式前后移动,以便用户可以看到选项卡中包含的字符串。 Android 正在执行此设置,当您的手机显示屏不足以显示整个标签时。

下面是在JavaFX中使用Service实现它的代码,但这不是一个好方法。

问题是: 这是我如何使用动画或 JavaFX 类中的其他构建来做到这一点?

代码:

import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import tools.InfoTool;

public class MoveTitleService extends Service<Void>{
    private String title;
    volatile boolean doAnimation;
    private int counter;
    public Label movingText = new Label("A reallyyy big teeeeexxxxxxxxxxxxxxxxxxxxxxxt");


    /**
     *Constructor
    */
    public MoveTitleService() {
         movingText.setFont(Font.font("null",FontWeight.BOLD,14));
         movingText.setTextFill(Color.WHITE);   

         setOnSucceeded( s ->{
             movingText.setText("");
         });
    }

    //Start the Service
    public void startTheService(String title) {
        this.title = title;
        doAnimation = true;

        restart();
    }


    //Stop the Service
    public void stopService(){
        doAnimation=false;
    }

    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {

                while (doAnimation) {
                    //System.out.println("MoveTitleService is Running...");

                    // One letter at a time
                    for (int m = 0; m <= title.length(); m++) {
                        counter=m;

                        Platform.runLater( () ->{
                            movingText.setText(title.substring(0, counter) + addSpaces(title.length() - counter));      
                        });

                        if(!doAnimation) break;
                        Thread.sleep(150);
                    }

                    // Disappearing to back
                    for (int m = 0; m < title.length(); m++) {
                        counter=m;

                        Platform.runLater( () ->{
                            movingText.setText(title.substring(counter));
                        });

                        if(!doAnimation) break;
                        Thread.sleep(150);
                    }

                    // Appearing to front
                    for (int m = 1; m <= title.length(); m++) {
                        counter=m;

                        Platform.runLater( () ->{
                            movingText.setText(title.substring(title.length() - counter));
                        });

                        if(!doAnimation) break;
                        Thread.sleep(150);
                    }

                    if(!doAnimation) break;
                    for(int i=0; i<3000/150; i++)
                        Thread.sleep(150);
                    Thread.sleep(3000);

                }

                return null;
            }

            private String addSpaces(int spaces) {
                String z = "";
                for (int i = 0; i <= spaces; i++)
                    z += " ";

                return z;
            }

        };
    }

}

最佳答案

您可以为此使用时间轴:

// min distance to Pane bounds
private static final double OFFSET = 25;

@Override
public void start(Stage primaryStage) {
    Text text = new Text("A reallyyy big teeeeexxxxxxxxxxxxxxxxxxxxxxxt!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    text.setLayoutY(25);
    text.setManaged(false);
    text.setLayoutX(OFFSET);

    Pane pane = new Pane(text);
    pane.setMinHeight(50);

    Timeline timeline = new Timeline();

    KeyFrame updateFrame = new KeyFrame(Duration.seconds(1 / 60d), new EventHandler<ActionEvent>() {

        private boolean rightMovement;

        @Override
        public void handle(ActionEvent event) {
            double tW = text.getLayoutBounds().getWidth();
            double pW = pane.getWidth();
            double layoutX = text.getLayoutX();

            if (2 * OFFSET + tW <= pW && layoutX >= OFFSET) {
                // stop, if the pane is large enough and the position is correct
                text.setLayoutX(OFFSET);
                timeline.stop();
            } else {
                if ((rightMovement && layoutX >= OFFSET) || (!rightMovement && layoutX + tW + OFFSET <= pW)) {
                    // invert movement, if bounds are reached
                    rightMovement = !rightMovement;
                }

                // update position
                if (rightMovement) {
                    layoutX += 1;
                } else {
                    layoutX -= 1;
                }
                text.setLayoutX(layoutX);
            }
        }
    });

    timeline.getKeyFrames().add(updateFrame);
    timeline.setCycleCount(Animation.INDEFINITE);

    // listen to bound changes of the elements to start/stop the animation
    InvalidationListener listener = o -> {
        double textWidth = text.getLayoutBounds().getWidth();
        double paneWidth = pane.getWidth();
        if (textWidth + 2 * OFFSET > paneWidth
                && timeline.getStatus() != Animation.Status.RUNNING) {
            timeline.play();
        }
    };

    text.layoutBoundsProperty().addListener(listener);
    pane.widthProperty().addListener(listener);

    Scene scene = new Scene(pane);

    primaryStage.setScene(scene);
    primaryStage.show();
}

请注意,需要自己重复更新位置,因为动画在运行时无法调整,因此要使任何大小调整在动画期间生效,您需要重复更新...

关于JavaFX 字幕动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39303349/

相关文章:

java - 为什么 JavaFX table.getItems().clear() 也会清除 ObservableList

JavaFX - 为不同TextArea的内容设置不同的背景

Java MongoDB访问对象子节点

java - 使用 wget 和 Nexus Rest API 进行奇怪的文件命名

java - 如何使用 ExpectJ 工具在 java 代码中运行 Unix shell 脚本?

java - 无法在构造函数中声明私有(private)变量?

iphone - 在非 cocos2d 项目中包含 cocos2d

css - 什么是离散动画?

animation - React 生命周期动画

java - 为什么 XYChart 中轴的父节点不是图表本身? (JavaFX)