JavaFXPorts - 移动设备上的滚动条问题
JavaFXPorts - Problems with ScrollBar on Mobile Devices
我目前正在使用 GluonHQ 和 JavaFXPorts 使用 JavaFX 开发移动应用程序。我的一个屏幕包含一个列表视图,您可以从下面的屏幕截图中看到,该屏幕截图来自我的 iPhone 6.
我注意到移动设备中的滚动条存在以下问题:
- 我第一次触摸屏幕时,滚动条出现了一点偏移,然后移动到正确的正确位置。这只是第一次发生很快。 (截图)
- 我注意到每次触摸屏幕时都会出现滚动条,而不仅仅是在触摸和拖动时出现。在本机 iOS 应用程序中,滚动条仅在您触摸和拖动时出现。如果您将手指放在屏幕上然后将其移开,则不会出现滚动条。
- 当我从屏幕上移开手指时,滚动条总是需要一些时间才能消失,而在本机应用程序中它会立即消失。
谁能帮我解决这些问题。如何定义滚动条再次隐藏之前出现的时间?
您可以通过创建一个 ListView 并加载一些项目来体验这种情况。
更新
感谢下面 Jose Pereda 的回答,我已经设法克服了上述所有三个问题。这是我用来达到预期结果的代码。 Watch this short video 快速了解新滚动条的显示和行为方式。再一次,何塞,你是老板!请继续提出任何改进意见。
public class ScrollBarView {
public static void changeView(ListView<?> listView) {
listView.skinProperty().addListener(new ChangeListener<Object>() {
private StackPane thumb;
private ScrollBar scrollBar;
boolean touchReleased = true, inertia = false;
@Override
public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
scrollBar = (ScrollBar) listView.lookup(".scroll-bar");
// "hide" thumb as soon as the scroll ends
listView.setOnScrollFinished(e -> {
if (thumb != null) {
touchReleased = true;
playAnimation();
} // if
});
// Fix for 1. When user touches first time, the bar is set invisible so that user cannot see it is
// placed in the wrong position.
listView.setOnTouchPressed(e -> {
if (thumb == null) {
thumb = (StackPane) scrollBar.lookup(".thumb");
thumb.setOpacity(0);
initHideBarAnimation();
} // if
});
// Try to play animation whenever an inertia scroll takes place
listView.addEventFilter(ScrollEvent.SCROLL, e -> {
inertia = e.isInertia();
playAnimation();
});
// As soon as the scrolling starts the thumb become visible.
listView.setOnScrollStarted(e -> {
sbTouchTimeline.stop();
thumb.setOpacity(1);
touchReleased = false;
});
} // changed
private Timeline sbTouchTimeline;
private KeyFrame sbTouchKF1, sbTouchKF2;
// Initialize the animation that hides the thumb when no scrolling takes place.
private void initHideBarAnimation() {
if (sbTouchTimeline == null) {
sbTouchTimeline = new Timeline();
sbTouchKF1 = new KeyFrame(Duration.millis(50), new KeyValue(thumb.opacityProperty(), 1));
sbTouchKF2 = new KeyFrame(Duration.millis(200), (e) -> inertia = false, new KeyValue(thumb.opacityProperty(), 0));
sbTouchTimeline.getKeyFrames().addAll(sbTouchKF1, sbTouchKF2);
} // if
} // initHideBarAnimation
// Play animation whenever touch is released, and when an inertia scroll is running but thumb reached its bounds.
private void playAnimation() {
if(touchReleased)
if(!inertia || (scrollBar.getValue() != 0.0 && scrollBar.getValue() != 1))
sbTouchTimeline.playFromStart();
} // playAnimation()
});
} // changeView
} // ScrollBarView
如评论所述,第一个问题是已知的,目前尚未修复。问题似乎与滚动条的初始宽度(在桌面中为 20 像素)有关,然后设置为 8 像素(在支持触摸的设备中),并移动到其最终位置,此可见偏移为 12 像素右边.
至于第二个和第三个问题,如果您不想自己修补和构建 JDK,可以覆盖默认行为,因为 ScrollBar
控件是一部分ListView 的 VirtualFlow
控件,两者都可以在运行时通过查找找到。
获得控件后,您可以根据需要调整其可见性。这个 属性 的唯一问题是它已经被绑定并不断地从 layoutChildren
方法中调用。
这是一个非常棘手的解决方案,但它适用于 2) 和 3):
public class BasicView extends View {
private final ListView<String> listView;
private ScrollBar scrollbar;
private StackPane thumb;
public BasicView(String name) {
super(name);
listView = new ListView<>();
// add your items
final InvalidationListener skinListener = new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (listView.getSkin() != null) {
listView.skinProperty().removeListener(this);
scrollbar = (ScrollBar) listView.lookup(".scroll-bar");
listView.setOnScrollFinished(e -> {
if (thumb != null) {
// "hide" thumb as soon as scroll/drag ends
thumb.setStyle("-fx-background-color: transparent;");
}
});
listView.setOnScrollStarted(e -> {
if (thumb == null) {
thumb = (StackPane) scrollbar.lookup(".thumb");
}
if (thumb != null) {
// "show" thumb again only when scroll/drag starts
thumb.setStyle("-fx-background-color: #898989;");
}
});
}
}
};
listView.skinProperty().addListener(skinListener);
setCenter(listView);
}
}
我目前正在使用 GluonHQ 和 JavaFXPorts 使用 JavaFX 开发移动应用程序。我的一个屏幕包含一个列表视图,您可以从下面的屏幕截图中看到,该屏幕截图来自我的 iPhone 6.
我注意到移动设备中的滚动条存在以下问题:
- 我第一次触摸屏幕时,滚动条出现了一点偏移,然后移动到正确的正确位置。这只是第一次发生很快。 (截图)
- 我注意到每次触摸屏幕时都会出现滚动条,而不仅仅是在触摸和拖动时出现。在本机 iOS 应用程序中,滚动条仅在您触摸和拖动时出现。如果您将手指放在屏幕上然后将其移开,则不会出现滚动条。
- 当我从屏幕上移开手指时,滚动条总是需要一些时间才能消失,而在本机应用程序中它会立即消失。
谁能帮我解决这些问题。如何定义滚动条再次隐藏之前出现的时间?
您可以通过创建一个 ListView 并加载一些项目来体验这种情况。
更新
感谢下面 Jose Pereda 的回答,我已经设法克服了上述所有三个问题。这是我用来达到预期结果的代码。 Watch this short video 快速了解新滚动条的显示和行为方式。再一次,何塞,你是老板!请继续提出任何改进意见。
public class ScrollBarView {
public static void changeView(ListView<?> listView) {
listView.skinProperty().addListener(new ChangeListener<Object>() {
private StackPane thumb;
private ScrollBar scrollBar;
boolean touchReleased = true, inertia = false;
@Override
public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
scrollBar = (ScrollBar) listView.lookup(".scroll-bar");
// "hide" thumb as soon as the scroll ends
listView.setOnScrollFinished(e -> {
if (thumb != null) {
touchReleased = true;
playAnimation();
} // if
});
// Fix for 1. When user touches first time, the bar is set invisible so that user cannot see it is
// placed in the wrong position.
listView.setOnTouchPressed(e -> {
if (thumb == null) {
thumb = (StackPane) scrollBar.lookup(".thumb");
thumb.setOpacity(0);
initHideBarAnimation();
} // if
});
// Try to play animation whenever an inertia scroll takes place
listView.addEventFilter(ScrollEvent.SCROLL, e -> {
inertia = e.isInertia();
playAnimation();
});
// As soon as the scrolling starts the thumb become visible.
listView.setOnScrollStarted(e -> {
sbTouchTimeline.stop();
thumb.setOpacity(1);
touchReleased = false;
});
} // changed
private Timeline sbTouchTimeline;
private KeyFrame sbTouchKF1, sbTouchKF2;
// Initialize the animation that hides the thumb when no scrolling takes place.
private void initHideBarAnimation() {
if (sbTouchTimeline == null) {
sbTouchTimeline = new Timeline();
sbTouchKF1 = new KeyFrame(Duration.millis(50), new KeyValue(thumb.opacityProperty(), 1));
sbTouchKF2 = new KeyFrame(Duration.millis(200), (e) -> inertia = false, new KeyValue(thumb.opacityProperty(), 0));
sbTouchTimeline.getKeyFrames().addAll(sbTouchKF1, sbTouchKF2);
} // if
} // initHideBarAnimation
// Play animation whenever touch is released, and when an inertia scroll is running but thumb reached its bounds.
private void playAnimation() {
if(touchReleased)
if(!inertia || (scrollBar.getValue() != 0.0 && scrollBar.getValue() != 1))
sbTouchTimeline.playFromStart();
} // playAnimation()
});
} // changeView
} // ScrollBarView
如评论所述,第一个问题是已知的,目前尚未修复。问题似乎与滚动条的初始宽度(在桌面中为 20 像素)有关,然后设置为 8 像素(在支持触摸的设备中),并移动到其最终位置,此可见偏移为 12 像素右边.
至于第二个和第三个问题,如果您不想自己修补和构建 JDK,可以覆盖默认行为,因为 ScrollBar
控件是一部分ListView 的 VirtualFlow
控件,两者都可以在运行时通过查找找到。
获得控件后,您可以根据需要调整其可见性。这个 属性 的唯一问题是它已经被绑定并不断地从 layoutChildren
方法中调用。
这是一个非常棘手的解决方案,但它适用于 2) 和 3):
public class BasicView extends View {
private final ListView<String> listView;
private ScrollBar scrollbar;
private StackPane thumb;
public BasicView(String name) {
super(name);
listView = new ListView<>();
// add your items
final InvalidationListener skinListener = new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (listView.getSkin() != null) {
listView.skinProperty().removeListener(this);
scrollbar = (ScrollBar) listView.lookup(".scroll-bar");
listView.setOnScrollFinished(e -> {
if (thumb != null) {
// "hide" thumb as soon as scroll/drag ends
thumb.setStyle("-fx-background-color: transparent;");
}
});
listView.setOnScrollStarted(e -> {
if (thumb == null) {
thumb = (StackPane) scrollbar.lookup(".thumb");
}
if (thumb != null) {
// "show" thumb again only when scroll/drag starts
thumb.setStyle("-fx-background-color: #898989;");
}
});
}
}
};
listView.skinProperty().addListener(skinListener);
setCenter(listView);
}
}