javafxgluonjavafx-webenginegluonfx

JavaFX Webview on Mobile does not receive any TouchEvent


I am trying to do a simple Demo about using JavaFX on Mobile using GluonFX Pluging & Attche.

The App, Will be Simply WebApp. The Main problem now is When the user click on any text input field, the Mobile keyboard is shown but most of times the View is not moved to keep the edited text field still shown.

I checked the Attach Service (Keyboard), The code by "José_Pereda" is great. but is not suitable for the full screen webview. because it shift the all view to be visible when the keyboard is up. but this mean that the input field may be out of the screen!

How can i solve this problem I tried to make touch/click event to check the last X,Y from the user and use this value in the keyboard service handler and this works great on Desktop (linux) but on Android phone, no Touchevent on the webview are fired !!

Any help


This is the Main Controller which have all the magic. but the Webview Object is shared in the App

package com.sam.stv.parentsapp;

import com.gluonhq.attach.keyboard.KeyboardService;
import com.gluonhq.attach.util.Platform;
import com.gluonhq.attach.util.Services;

import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TouchEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebView;
import javafx.util.Duration;

public class PrimaryController {

    WebView webview;

    private EventHandler<MouseEvent> handlerForMouse;
    private EventHandler<TouchEvent> handlerForTouch;

    double lastX;
    double lastY;

    SimpleStringProperty msg = new SimpleStringProperty("Good Luck!");

    ProgressIndicator progressIndicator;// = new ProgressIndicator(); // or you can use ImageView with animated gif

    @FXML
    StackPane webcontainer;
    @FXML
    StackPane paneBottom;
    @FXML
    StackPane paneTop;

    @FXML
    private void initialize() {
        // ========================================= Logging lbl in Mobile
        Label lbl = new Label();
        lbl.textProperty().bind(msg);
        lbl.prefHeight(50);
        lbl.prefWidth(200);
        paneTop.getChildren().addAll(lbl);
        // ========================================= WebView Setup
        this.webview = App.getWebview(); 
        webcontainer.getChildren().add(webview);
        // ========================================= handling Touching and clicking by
        // adding listeners
        initHandlers();
        handlingTouching();
        // ========================================= Attache-Service:=> Virtual Keyboard
        // Handling
        Services.get(KeyboardService.class).ifPresent(service -> {
            service.visibleHeightProperty().addListener((obs, ov, nv) -> doSomethingForKeyboardBasedOnTouch(nv));
        });

    }

    /**
     * This method shit the View (webview) upward Based on the last user touch/click
     * point position.
     * 
     * @param nv
     */
    void doSomethingForKeyboardBasedOnTouch(Number nv) {
        Node node = this.webview;
        double kh = nv.doubleValue();
        double padding = 20;

        if (node == null || node.getScene() == null || node.getScene().getWindow() == null) {
            return;
        }
        double tTot = node.getScene().getHeight();
        // Original Code :-> node.getLocalToSceneTransform().getTy() + node.getBoundsInParent().getHeight() + 2;
        double ty = lastY + padding;  // Can not update this value using the last touch event
        double y = 1;
        Parent root = node.getScene().getRoot();

        if (ty > tTot - kh) {
            y = tTot - ty - kh;
        } else if (kh == 0 && root.getTranslateY() != 0) {
            y = 0;
        }
        if (y <= 0) {
            System.out.println(String.format("Moving %s %.2f pixels", root, y));

            final TranslateTransition transition = new TranslateTransition(Duration.millis(50), root);
            transition.setFromY(root.getTranslateY());
            transition.setToY(y);
            transition.setInterpolator(Interpolator.EASE_OUT);
            transition.playFromStart();
        }
    }

    private void handlingTouching() {
        applyTouchClickInput(webcontainer);
        applyTouchClickInput(paneBottom);
        applyTouchClickInput(paneTop);
        applyTouchClickInput(webview);
    }

    private void applyTouchClickInput(Node drawSurface) {

        if (Platform.isAndroid() || Platform.isIOS()) {
            // end the path when mouse released event
//          drawSurface.setOnTouchReleased(handlerForTouch);
//          drawSurface.setOnTouchPressed(handlerForTouch);
//          drawSurface.setOnTouchMoved(handlerForTouch);
//          drawSurface.addEventFilter(TouchEvent.ANY, handlerForTouch);
//          drawSurface.addEventHandler(TouchEvent.ANY, handlerForTouch);
            drawSurface.addEventFilter(TouchEvent.TOUCH_MOVED, handlerForTouch);
            drawSurface.addEventFilter(TouchEvent.TOUCH_PRESSED, handlerForTouch);
            drawSurface.addEventFilter(TouchEvent.TOUCH_RELEASED, handlerForTouch);
//          drawSurface.addEventHandler(TouchEvent.TOUCH_MOVED, handlerForTouch);
//          drawSurface.addEventHandler(TouchEvent.TOUCH_PRESSED, handlerForTouch);
//          drawSurface.addEventHandler(TouchEvent.TOUCH_RELEASED, handlerForTouch);
        } else if (Platform.isDesktop()) {
            drawSurface.addEventFilter(MouseEvent.MOUSE_CLICKED, handlerForMouse);

        }
    }

    void initHandlers() {
        this.handlerForMouse = new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {
                String log = "Clicking: " + event.getEventType().getName() + " on "
                        + event.getTarget().getClass().getSimpleName() + ")--->(" + event.getX() + "," + event.getY()
                        + ")";
                logging(log);
                logTouch(event.getX(), event.getY());
            }
        };

        this.handlerForTouch = new EventHandler<TouchEvent>() {

            @Override
            public void handle(TouchEvent event) {
                String log = "Touching: " + event.getEventType().getName() + " on "
                        + event.getTarget().getClass().getSimpleName() + ")--->(" + event.getTouchPoint().getX() + ","
                        + event.getTouchPoint().getY() + ")";
                logging(log);
                logTouch(event.getTouchPoint().getX(), event.getTouchPoint().getY());
            }
        };

    }

    void logTouch(double x, double y) {
        System.out.println("Set New X & Y (" + lastX + "," + lastY + ")--->(" + x + "," + y + ")");
        lastX = x;
        lastY = y;
    }

    public void logging(String log) {
        System.out.println("===============================================> General Logging Handler.");
        System.out.println(log);
        msg.set(log);
    }

}


Solution

  • Based on @JosePereda Confirmation that we can not receive the touch event on the Mobile devices.

    Then, my work around to bypass that is to inject a Javascript in the webview loading time. this script is recording the touch event and then call a Java method whenever this touch event is done.

    The Javascript-Java bridging is not work flexibly as expected, but works