[转载] JavaFX ScrollPane滚动到指定位置
2016-06-17JavaFX攻城狮3688°c
A+ A-原文地址:http://blog.csdn.net/alanzyy/article/details/50859403
背景说明
本人有一个任务管理工具管理自己每天的工作计划,按照自己设定的任务按时按量完成工作。
如上图,这里展示的一周的任务内容,也就是有7个任务块的内容水平排列,出现滚动条是必然的。为了让我直观查看一周的内容更加便捷,而不是必须使用细长的滚动条,我想做以下两点:
1) 左右增加两个悬浮按钮,点击可以将滚动条滚到最左边或者最右边(一般我的这些内容只有两屏,所以点一下右边按钮相当于切屏,正好适用)
2) 点击某个任务块的日期时,如果该块内容没有完全显示出来,则将滚动条滚到合适位置将内容完全呈现(我在做这一点时,一开始以为ScrollPane有原生支持,提供了方法将给定的一个Node直接滚动到将其完全显示,我记得Swing中是有的,但是可惜JavaFX没有,我才不得不花了2小时研究滚动条值的计算原理)
注意,我这里只做了水平方向,竖直方向的原理应该差不多。
一、增加两个悬浮按钮
【知识点1】:ScrollPane的水平滚动条值HValue和垂直滚动条值VValue都是Double类型,取值范围是[0, 1],0表示最左边或者最上面,1表示最右边或者最下面
增加两个悬浮按钮的原理如下:
使用StackPane作为容器,底下先放置任务块的内容面板,上面再放置悬浮按钮所在的面板
将上面的按钮悬浮面板设置不响应鼠标事件,这样上层的面板不会影响底层的内容面板响应事件
在点击左边的按钮时,将ScrollPane的HValue设置为0,使滚动条滚动到最左边
在点击右边的按钮时,将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
用文字来说就是,最小宽度除以待滚动宽度,或者最大宽度减去可见宽度后再除以待滚动宽度。
根据以上的知识,我们可以使用以下步骤进行处理:
当点击任务块日期时,传出一个Change事件,在事件中传出该任务块相对于ScrollPane的Bounds
获取ScrollPane的可见宽度,待滚动宽度
判断任务块的Bounds是属于超出左边还是超出右边,超出左边使用minX公式,超出右边使用maxX公式
计算出合适的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/