你好,欢迎访问我的博客!登录
当前位置:首页 - JavaFX - 正文 求知成瘾,却无作品!

[转载] JavaFX ScrollPane滚动到指定位置

2016-06-17JavaFX攻城狮1611°c
A+ A-

原文地址:http://blog.csdn.net/alanzyy/article/details/50859403

背景说明

本人有一个任务管理工具管理自己每天的工作计划,按照自己设定的任务按时按量完成工作。

week-task.png

如上图,这里展示的一周的任务内容,也就是有7个任务块的内容水平排列,出现滚动条是必然的。为了让我直观查看一周的内容更加便捷,而不是必须使用细长的滚动条,我想做以下两点:

1) 左右增加两个悬浮按钮,点击可以将滚动条滚到最左边或者最右边(一般我的这些内容只有两屏,所以点一下右边按钮相当于切屏,正好适用)

2) 点击某个任务块的日期时,如果该块内容没有完全显示出来,则将滚动条滚到合适位置将内容完全呈现(我在做这一点时,一开始以为ScrollPane有原生支持,提供了方法将给定的一个Node直接滚动到将其完全显示,我记得Swing中是有的,但是可惜JavaFX没有,我才不得不花了2小时研究滚动条值的计算原理)

注意,我这里只做了水平方向,竖直方向的原理应该差不多。

一、增加两个悬浮按钮

【知识点1】:ScrollPane的水平滚动条值HValue和垂直滚动条值VValue都是Double类型,取值范围是[0, 1],0表示最左边或者最上面,1表示最右边或者最下面

增加两个悬浮按钮的原理如下:

  1. 使用StackPane作为容器,底下先放置任务块的内容面板,上面再放置悬浮按钮所在的面板

  2. 将上面的按钮悬浮面板设置不响应鼠标事件,这样上层的面板不会影响底层的内容面板响应事件

  3. 在点击左边的按钮时,将ScrollPane的HValue设置为0,使滚动条滚动到最左边

  4. 在点击右边的按钮时,将ScrollPane的HValue设置为1,使滚动条滚动到最右边

代码如下:

private void initGlassPane() {
    BorderPane glass = new BorderPane();
    final Button btnLeft = new Button("<");
    btnLeft.setPrefHeight(200);
    btnLeft.getStyleClass().add("transparent");
    btnLeft.setOnAction((ActionEvent event) -> {
        scrollToLeft();
    });
    glass.setLeft(btnLeft);
    BorderPane.setAlignment(btnLeft, Pos.CENTER);
  
    final Button btnRight = new Button(">");
    btnRight.setPrefHeight(200);
    btnRight.getStyleClass().add("transparent");
    btnRight.setOnAction((ActionEvent event) -> {
        scrollToRight();
    });
    glass.setRight(btnRight);
    BorderPane.setAlignment(btnRight, Pos.CENTER);
  
    glass.setPickOnBounds(false);

    btnLeft.setVisible(false);
    scrollPane.hvalueProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
        btnLeft.setVisible(newValue.doubleValue() != 0);
        btnRight.setVisible(newValue.doubleValue() != 1);
    });

    this.getChildren().add(glass);
}

public final void scrollToLeft() {
    scrollPane.setHvalue(0);
}

public final void scrollToRight() {
    scrollPane.setHvalue(1);
}

二、点击任务块日期完全呈现内容

【知识点2】:scrollPane.getViewportBounds()方法返回Bounds对象,表示ScrollPane中当前显示区域的大小,这个Bounds对象的Width和Height是正确的,但是其X和Y坐标的规则含糊,并不是相对于ScrollPane的位置,也不是相对于屏幕的位置,而且滚动条值变化时该Bounds对象的值不一定变化

【知识点3】:滚动条值与显示内容位置的关系(滚动条内容Content,显示内容Viewport):

待滚动宽度ScrollWidth = Content.Width - Viewport.Width

水平滚动条值HValue = Viewport.minX / ScrollWidth ,或

水平滚动条值HValue = (Viewport.maxX - Viewport.Width) / ScrollWidth

用文字来说就是,最小宽度除以待滚动宽度,或者最大宽度减去可见宽度后再除以待滚动宽度。

根据以上的知识,我们可以使用以下步骤进行处理:

  1. 当点击任务块日期时,传出一个Change事件,在事件中传出该任务块相对于ScrollPane的Bounds

  2. 获取ScrollPane的可见宽度,待滚动宽度

  3. 判断任务块的Bounds是属于超出左边还是超出右边,超出左边使用minX公式,超出右边使用maxX公式

  4. 计算出合适的HValue时,设置给ScrollPane,scrollPane.setHvalue()

代码如下:

BoundsCheck boundsCheck = (Bounds bounds) -> {
    final Bounds visibleBounds = scrollPane.getViewportBounds();
    double totalWidth = scrollPane.getContent().getBoundsInLocal().getWidth();//总宽度
    double visibleWidth = visibleBounds.getWidth();//可见宽度
    double scrollWidth = totalWidth - visibleWidth;//待滚动宽度
    double hvalue = scrollPane.getHvalue();
    double maxX = visibleWidth + hvalue * scrollWidth;//当前显示的maxX

    if (bounds.getMinX() < maxX - visibleWidth) {//超出左边
        double x = bounds.getMinX();
        scrollPane.setHvalue(x / scrollWidth);
    } else if (bounds.getMaxX() > maxX) {//超出右边
        double x = bounds.getMaxX() - visibleWidth;
        scrollPane.setHvalue(x / scrollWidth);
    }
};

到此,我的目标实现了!这3个知识点对你有用吗?

http://www.alanz.me/2016/03/javafx-scrollpane-value/


标签:

发表评论

必填

选填

选填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。


  登录