Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8210542

Difficult to select an item in a ListView with touch

XMLWordPrintable

    • x86_64
    • windows_10

      ADDITIONAL SYSTEM INFORMATION :
      Java 9 and above.

      A DESCRIPTION OF THE PROBLEM :
      On touch devices it is difficult to select an item in a ListView as even minimal movements of the finger on press will prevent the selection from happening.

      Cause of the bug: The current implementation of com.sun.javafx.scene.control.behavior.CellBehaviorBase does NOT update the list selection, if a MOUSE_DRAGGED happens between (synthesized) MOUSE_PRESSED and (synthesized) MOUSE_RELEASED events. But as MOUSE_DRAGGED events are fired even if the mouse (here: finger) is moved just one single pixel, and as such small jitter might easily happen on touch devices, it is difficult for the user to select an item on the first try.
      We found out that on some touch devices this bug is less relevant than on others. We think that these devices perform some pre-processing on touch events to reduce jitter. As an effect, touch-selection works most of the time (but not always) as expected on these devices.

      Solution Suggestion: Selection should rather be omitted on DRAG_DETECTED (which respects some movement threshold) than on MOUSE_DRAGGED.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      (1) Execute the demo application listed below on a touch-enabled device.
      (2) Try to select items via touch. Slightly roll your finger while touching etc.
      (3) Make sure, the synthesized flag is set on the MouseEvents. Therefore, mouse event logging can be enabled by the checkbox at the left bottom of the application.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Selecting items is easily possible and works all the time, even if the finger is not pressed in a clean manner (e.g. the finger moves or rolls slightly).
      ACTUAL -
      Most of the time, selection does not happen, as MOUSE_DRAGGED events intercept the selection gesture.
      Only if the finger touches the ListView in a clear manner, selection works.

      ---------- BEGIN SOURCE ----------
      package selectionbug;

      import java.util.List;
      import java.util.stream.Collectors;
      import java.util.stream.IntStream;

      import javafx.application.Application;
      import javafx.beans.value.ChangeListener;
      import javafx.event.EventHandler;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.CheckBox;
      import javafx.scene.control.ListView;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.layout.BorderPane;
      import javafx.stage.Stage;

      public class ListViewApplication extends Application {

      private final EventHandler<MouseEvent> mouseEventHandler = System.out::println;
      private final ChangeListener<String> selectionChangedListener = (p, o, n) -> System.out
      .format("selection changed from %s to %s%n", o, n);

      @Override
      public void start(Stage primaryStage) throws Exception {
      // dummy data
      List<String> items = IntStream.range(0, 10)
      .mapToObj(i -> String.format("item #%d", i))
      .collect(Collectors.toList());

      ListView<String> listView = new ListView<>();
      listView.setId("list");
      listView.getItems().addAll(items);
      listView.getSelectionModel().selectedItemProperty().addListener(selectionChangedListener);

      CheckBox enableEventLoggingCheckbox = new CheckBox("Log mouse events");

      BorderPane root = new BorderPane();
      root.setCenter(listView);
      root.setBottom(enableEventLoggingCheckbox);
      root.setStyle("-fx-font-size:24pt");

      enableEventLoggingCheckbox.selectedProperty().addListener((p, o, n) -> {
      if (n) {
      addEventFilters(listView);
      } else {
      removeEventFilters(listView);
      }
      });

      Scene scene = new Scene(root, 600, 600);

      primaryStage.setScene(scene);
      primaryStage.show();
      }

      private void addEventFilters(Node node) {
      node.addEventFilter(MouseEvent.MOUSE_PRESSED, mouseEventHandler);
      node.addEventFilter(MouseEvent.MOUSE_DRAGGED, mouseEventHandler);
      node.addEventFilter(MouseEvent.DRAG_DETECTED, mouseEventHandler);
      node.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseEventHandler);
      }

      private void removeEventFilters(Node node) {
      node.removeEventFilter(MouseEvent.MOUSE_PRESSED, mouseEventHandler);
      node.removeEventFilter(MouseEvent.MOUSE_DRAGGED, mouseEventHandler);
      node.removeEventFilter(MouseEvent.DRAG_DETECTED, mouseEventHandler);
      node.removeEventFilter(MouseEvent.MOUSE_RELEASED, mouseEventHandler);
      }

      public static void main(String[] args) {
      Application.launch(args);
      }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      No real workaround found so far. The only reasonable workaround is overwriting and fixing the behavior of the cell which is not possible, as the behavior classes are internal API and cannot be overwritten since Java 9.
      Currently, we are fixing this issue (as suggested above) by ourselves by building JavaFx on our own.

      FREQUENCY : always


            aghaisas Ajit Ghaisas
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: