JavaFXPorts - Проблемы с ScrollBar на мобильных устройствах

В настоящее время я разрабатываю мобильное приложение с JavaFX, используя GluonHQ и JavaFXPorts. Один из моих экранов содержит вид списка, как вы можете видеть на скриншоте ниже, который был взят с моего iPhone 6.

Я заметил следующие проблемы с полосой прокрутки в мобильных устройствах:

  1. В первый раз, когда я касаюсь экрана, полоса прокрутки появляется немного не на своем месте, а затем перемещается в правильное положение. Это происходит быстро только в первый раз. (Скриншот)
  2. Я заметил, что полоса прокрутки появляется каждый раз, когда я касаюсь экрана, а не только когда я касаюсь и перетаскиваю. В родных приложениях iOS полоса прокрутки появляется только при касании и перетаскивании. Если вы удерживаете палец на экране, а затем убираете его, полоса прокрутки не появляется.
  3. Полоса прокрутки всегда исчезает, когда я убираю палец с экрана, тогда как в родных приложениях она исчезает мгновенно.

Может кто-нибудь помочь мне в решении этих проблем. Как определить время появления полосы прокрутки, прежде чем она снова скроется?

Вы можете испытать эту ситуацию, просто создав 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);
    }

}
Другие вопросы по тегам