JavaFX ListView时间轴滚动动画跳动(不稳定) [英] JavaFX ListView Timeline Scroll Animation is Jumpy (choppy)

查看:185
本文介绍了JavaFX ListView时间轴滚动动画跳动(不稳定)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用JavaFX ListView和时间轴来动画列表的滚动.滚动动画较慢时,文本会跳动(不连贯).我尝试使用AnimationTimer滚动文本.在缓慢滚动时,文本也跳动(不连贯). ListView控件对于虚拟视口特征是必需的.以下是使用Java版本1.8在我的Mac上重新创建问题的示例.

I'm using a JavaFX ListView and Timeline to animate scrolling of a list. When the scroll animation is slow the text is jumpy (choppy). I have tried using an AnimationTimer to scroll the text. The text was also jumpy (choppy) during slow scrolling. The ListView control is necessary for the virtual viewport characteristics. Following is an example that recreates the problem on my Mac using Java Version 1.8.

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.List;

public class Jumpy extends Application {

    ListView listView;
    Timeline timeline = new Timeline();
    double speed = 0.0000005;

    @Override
    public void start(Stage stage) throws Exception {
        List list = new ArrayList();
        for (int i = 0; i < 2000; i++) {
            Text text = new Text("Random line of text to show how it is choppy during scroll animation");
            text.setStyle("-fx-font-size: " + 4 + "em");
            list.add(text);
        }
        ObservableList observableList = FXCollections.observableList(list);
        listView = new ListView((observableList));
        listView.setPrefWidth(600);

        AnchorPane root = new AnchorPane();
        root.getChildren().addAll(listView, buttons());

        stage.setScene(new Scene(root));
        stage.show();
    }

    public void scroll() {
        ScrollBar scrollBar = getVerticalScrollBar();
        EventHandler scroll = new EventHandler<ActionEvent>() {
            public void handle(ActionEvent t) {
                scrollBar.setValue(scrollBar.getValue() + speed);
            }
        };

        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.setAutoReverse(false);
        KeyValue kv = new KeyValue(scrollBar.valueProperty(), 1);
        KeyFrame kf = new KeyFrame(Duration.seconds(0.017), scroll);
        timeline.getKeyFrames().add(kf);
        timeline.play();
    }

    public ScrollBar getVerticalScrollBar() {
        ScrollBar scrollBar = null;
        for (Node node : listView.lookupAll(".scroll-bar")) {
            if (node instanceof ScrollBar) {
                scrollBar = (ScrollBar) node;
                if ((scrollBar.getOrientation().compareTo(Orientation.VERTICAL)) == 0) {
                    break;
                }
            }
        }
        return scrollBar;
    }

    public HBox buttons() {
        HBox hBox = new HBox();
        Button start = new Button("start");
        start.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                scroll();
            }
        });
        Button slower = new Button("slower");
        slower.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                ScrollBar scrollBar = getVerticalScrollBar();
                EventHandler scroll = new EventHandler<ActionEvent>() {
                    public void handle(ActionEvent t) {
                        scrollBar.setValue(scrollBar.getValue() - 0.000001);
                    }
                };
                KeyFrame kf = new KeyFrame(Duration.millis(10.0D), scroll);
                timeline.getKeyFrames().add(kf);
            }

        });
        Button faster = new Button("faster");
        faster.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                ScrollBar scrollBar = getVerticalScrollBar();
                EventHandler scroll = new EventHandler<ActionEvent>() {
                    public void handle(ActionEvent t) {
                        scrollBar.setValue(scrollBar.getValue() + 0.000001);
                    }
                };
                KeyFrame kf = new KeyFrame(Duration.millis(10.0D), scroll);
                timeline.getKeyFrames().add(kf);

            }
        });
        hBox.getChildren().addAll(start, slower, faster);
        return hBox;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

推荐答案

如果仅用2000个Node实例填充虚拟控件,那么使用虚拟控件是没有意义的:您几乎完全破坏了使用虚拟控件的所有好处.首先是虚拟化.

There's no point in using a virtualized control if you're just going to populate it with 2000 Node instances: you completely destroy almost all the benefits of using the virtualization in the first place.

使用数据填充控件(例如,在本例中为String s),然后为ListView设置样式或使用单元格工厂控制值的显示方式.

Populate the control with data (e.g., in this case, Strings) and either style the ListView or use a cell factory to control how the values are displayed.

以下内容对我来说要好得多

The following performs much better for me:

ListView<String> listView;
Timeline timeline = new Timeline();
double speed = 0.0000005;

@Override
public void start(Stage stage) throws Exception {
    List<String> list = new ArrayList<>();
    for (int i = 0; i < 2000; i++) {
        String text = "Random line of text to show how it is choppy during scroll animation";
        //  text.setStyle("-fx-font-size: " + 4 + "em");
        list.add(text);
    }
    ObservableList<String> observableList = FXCollections.observableList(list);
    listView = new ListView<String>((observableList));
    listView.setPrefWidth(600);

    listView.setStyle("-fx-font-size: 4em; ");

    AnchorPane root = new AnchorPane();
    root.getChildren().addAll(listView, buttons());

    stage.setScene(new Scene(root));
    stage.show();
}

此更改之后,使用AnimationTimer似乎仍然更加流畅.这是使用此方法的示例(并删除了所有冗余代码):

After this change, using an AnimationTimer seems slightly smoother still. Here's an example using this approach (and with all the redundant code removed):

import java.util.ArrayList;
import java.util.List;

import javafx.animation.AnimationTimer;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Jumpy extends Application {

    ListView<String> listView;
    Timeline timeline = new Timeline();
    double increment = 2e-5 ;
    double speed = 5*increment ;

    AnimationTimer timer = new AnimationTimer() {

        private long lastUpdate = -1 ;
        private ScrollBar scrollbar ;

        @Override
        public void start() {
            scrollbar = getVerticalScrollBar();
            super.start();
        }

        @Override
        public void handle(long now) {
            if (lastUpdate < 0) {
                lastUpdate = now ;
                return ;
            }

            long elapsedNanos = now - lastUpdate ;
            double delta = speed * elapsedNanos / 1_000_000_000 ;
            scrollbar.setValue(scrollbar.getValue() + delta);

            lastUpdate = now ;
        }
    };

    @Override
    public void start(Stage stage) throws Exception {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 2000; i++) {
            String text = "Random line of text to show how it is choppy during scroll animation";
            list.add(text);
        }
        ObservableList<String> observableList = FXCollections.observableList(list);
        listView = new ListView<String>((observableList));
        listView.setPrefWidth(600);

        listView.setStyle("-fx-font-size: 4em; ");

        AnchorPane root = new AnchorPane();
        root.getChildren().addAll(listView, buttons());

        stage.setScene(new Scene(root));
        stage.show();
    }

    private ScrollBar getVerticalScrollBar() {
        ScrollBar scrollBar = null;
        for (Node node : listView.lookupAll(".scroll-bar")) {
            if (node instanceof ScrollBar) {
                scrollBar = (ScrollBar) node;
                if (scrollBar.getOrientation() == Orientation.VERTICAL) {
                    break;
                }
            }
        }
        return scrollBar;
    }

    private HBox buttons() {
        HBox hBox = new HBox();
        Button start = new Button("start");
        start.setOnAction(event -> timer.start());
        Button slower = new Button("slower");
        slower.setOnAction(event -> speed -= increment);
        Button faster = new Button("faster");
        faster.setOnAction(event -> speed += increment);
        hBox.getChildren().addAll(start, slower, faster);
        return hBox;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这篇关于JavaFX ListView时间轴滚动动画跳动(不稳定)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆