且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

JavaFX ListView时间轴滚动动画跳动(不稳定)

更新时间:2022-01-11 04:19:36

如果仅用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);
    }
}