JavaFXPorts - Проблемы с ScrollBar на мобильных устройствах
В настоящее время я разрабатываю мобильное приложение с JavaFX, используя GluonHQ и JavaFXPorts. Один из моих экранов содержит вид списка, как вы можете видеть на скриншоте ниже, который был взят с моего iPhone 6.
Я заметил следующие проблемы с полосой прокрутки в мобильных устройствах:
- В первый раз, когда я касаюсь экрана, полоса прокрутки появляется немного не на своем месте, а затем перемещается в правильное положение. Это происходит быстро только в первый раз. (Скриншот)
- Я заметил, что полоса прокрутки появляется каждый раз, когда я касаюсь экрана, а не только когда я касаюсь и перетаскиваю. В родных приложениях iOS полоса прокрутки появляется только при касании и перетаскивании. Если вы удерживаете палец на экране, а затем убираете его, полоса прокрутки не появляется.
- Полоса прокрутки всегда исчезает, когда я убираю палец с экрана, тогда как в родных приложениях она исчезает мгновенно.
Может кто-нибудь помочь мне в решении этих проблем. Как определить время появления полосы прокрутки, прежде чем она снова скроется?
Вы можете испытать эту ситуацию, просто создав ListView и загрузив его с некоторыми элементами.
ОБНОВИТЬ
Благодаря ответу Хосе Переда, приведенному ниже, мне удалось преодолеть все три проблемы, описанные выше. Вот код, который я использовал для достижения желаемых результатов. Посмотрите это короткое видео, чтобы быстро понять, как выглядит и работает новая полоса прокрутки. Снова, Хосе, ты начальник! Пожалуйста, продолжайте любые комментарии для улучшения.
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
1 ответ
Как уже упоминалось в комментариях, первая проблема известна, и пока она не устранена. Кажется, что проблема связана с начальной шириной полосы прокрутки (20 пикселей, как на рабочем столе), а затем устанавливается на 8 пикселей (как в устройствах с сенсорным экраном) и перемещается в конечное положение с видимым сдвигом в 12 пикселей на право.
Что касается второй и третьей проблем, если вы не хотите самостоятельно устанавливать и исправлять JDK, можно переопределить поведение по умолчанию, так как ScrollBar
контроль является частью VirtualFlow
управление ListView, и оба могут быть найдены во время выполнения через поиск.
Когда у вас есть контроль, вы можете играть с его видимостью в соответствии с вашими потребностями. Единственная проблема с этим свойством состоит в том, что оно уже связано и постоянно вызывается из 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);
}
}