JavaFX中ListView的平滑滚动

5

我想知道是否有一种方法可以微调JavaFX的 ListView 的滚动。

你可能会说,这是因为我的集合太大了,或者我的 CellFactory 太重了…即使只有一小部分字符串集合,帧率也似乎太低了...

所以我想知道是否有某个设置或指南来实现流畅的滚动。

先行谢过。

1个回答

3
这是为ScrollPane创建的JFoenix平滑滚动效果, 但稍作修改后适用于ListViews(也适用于JFoenix的JFXListView)。
public class JFXSmoothScroll  {



private static ScrollBar getScrollbarComponent(ListView<?> control, Orientation orientation) {
    Node n = control.lookup(".scroll-bar");
    if (n instanceof ScrollBar) {
        final ScrollBar bar = (ScrollBar) n;
        if (bar.getOrientation().equals(orientation)) {
            return bar;
        }
    }

    return null;
}   

public static void smoothScrollingListView(ListView<?> listView, double speed) {
    smoothScrollingListView(listView, speed, Orientation.VERTICAL, bounds -> bounds.getHeight());
}

public static void smoothHScrollingListView(ListView<?> listView, double speed) {
    smoothScrollingListView(listView, speed, Orientation.HORIZONTAL, bounds -> bounds.getHeight());
}

private  static void smoothScrollingListView(ListView<?> listView, double speed, Orientation orientation, Function<Bounds, Double> sizeFunc) {
    ScrollBar scrollBar = getScrollbarComponent(listView, orientation);
    if (scrollBar == null) {
        return;
    }
    scrollBar.setUnitIncrement(5);
    final double[] frictions = {0.99, 0.1, 0.05, 0.04, 0.03, 0.02, 0.01, 0.04, 0.01, 0.008, 0.008, 0.008, 0.008, 0.0006, 0.0005, 0.00003, 0.00001};
    final double[] pushes = {speed};
    final double[] derivatives = new double[frictions.length];
    final double[] lastVPos = {0};
    Timeline timeline = new Timeline();
    final EventHandler<MouseEvent> dragHandler = event -> timeline.stop();
    final EventHandler<ScrollEvent> scrollHandler = event -> {
        scrollBar.valueProperty().set(lastVPos[0]);
        if (event.getEventType() == ScrollEvent.SCROLL) {
            double direction = event.getDeltaY() > 0 ? -1 : 1;
            for (int i = 0; i < pushes.length; i++) {
                derivatives[i] += direction * pushes[i];
            }
            if (timeline.getStatus() == Animation.Status.STOPPED) {
                timeline.play();
            }

        }
        event.consume();
    };
    if (scrollBar.getParent() != null) {
        scrollBar.getParent().addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
        scrollBar.getParent().addEventHandler(ScrollEvent.ANY, scrollHandler);
    }
    scrollBar.parentProperty().addListener((o,oldVal, newVal)->{
        if (oldVal != null) {
            oldVal.removeEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
            oldVal.removeEventHandler(ScrollEvent.ANY, scrollHandler);
        }
        if (newVal != null) {
            newVal.addEventHandler(MouseEvent.DRAG_DETECTED, dragHandler);
            newVal.addEventHandler(ScrollEvent.ANY, scrollHandler);
        }
    });

    timeline.getKeyFrames().add(new KeyFrame(Duration.millis(3), (event) -> {
        for (int i = 0; i < derivatives.length; i++) {
            derivatives[i] *= frictions[i];
        }
        for (int i = 1; i < derivatives.length; i++) {
            derivatives[i] += derivatives[i - 1];
        }
        double dy = derivatives[derivatives.length - 1];
        double size = sizeFunc.apply(scrollBar.getLayoutBounds());
        scrollBar.valueProperty().set(Math.min(Math.max(scrollBar.getValue() + dy / size, 0), 1));
        lastVPos[0] = scrollBar.getValue();
        if (Math.abs(dy) < 1) {
            if (Math.abs(dy) < 0.001) {
                timeline.stop();
            } 
        } 
    }));
    timeline.setCycleCount(Animation.INDEFINITE);       
}

}

这有点糟糕

final double[] lastVPos = {0};

因为某种原因,在触发ScrollEvent之前,我找不到触发事件的方法,并在此之前设置了初始滚动值。必须在第一次初始化列表后调用该方法,否则它将无法找到滚动条。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接