用于ListView的JavaFX平滑滚动

时间:2016-01-28 15:20:17

标签: java listview optimization javafx smooth-scrolling

我想知道是否有办法调整JavaFX ListView的滚动。

你可能会说这是滞后的,因为我的收藏太大和/或我的CellFactory太重了......只是帧速率看起来太低了,即使收集了很少的字符串...

所以我想知道是否有某个设置或指南要遵循以实现平滑滚动。

提前谢谢

1 个答案:

答案 0 :(得分:1)

这是JFoenix's smooth scrolling created for ScrollPanes,但为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);       
}

}

这有点hacky

final double[] lastVPos = {0};

由于某种原因,我无法在触发ScrollEvent之前找到触发器,并在此之前设置了初始滚动值。 第一次初始化列表后,需要调用方法,否则将找不到滚动条