0

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);
    }

}

SamFX
  • 19
  • 6
  • @josé-pereda, Any Idea? – SamFX Sep 03 '22 at 14:56
  • Have you tried listening to mouse events on mobile (instead of touch events)? – José Pereda Sep 03 '22 at 19:58
  • @JoséPereda, Yes I did, Did not work. And i added one more empty stackPane and some other controls (for testing), it was receiving the touch event perfectly. but the problem is on the webview only. Also, the mouse event is working perfectly om Lunix Desktop over the webview !! – SamFX Sep 03 '22 at 22:12
  • Can you post the relevant code (including the WebView) that you are using ? How does the WebView relate to the keyboard service? – José Pereda Sep 03 '22 at 22:35
  • As you can see, I tried all the event trigger method, and all of them worked in the testing Pane but not on the webview. and in Desktop, the mouse event is working everywhere – SamFX Sep 04 '22 at 03:35
  • any updates here ? – SamFX Sep 06 '22 at 18:10
  • There is a (long) reason that explains why it doesn't work. But in short, the JavaFX WebView node on Android is just "an empty container" where a native Android browser is displayed. Mouse/touch events are consumed by it, and those are not routed back to JavaFX. You shouldn't need to use the KeyboardService for anything related to the WebView, it will natively handle the keyboard. – José Pereda Sep 07 '22 at 19:34
  • Thank you for your answer, you are our FX hero,I was waiting it. – SamFX Sep 07 '22 at 20:04
  • Actually, the keyboard is already appeared when you click on input field, but the field is hidden behind the keyboard. Based on your code of the keyboard service I got an idea to not keep the whole view visible , but to record the last touch x,y and keep this touch point alive, that's why I wanted to record the x,y. Now I solved it by using injected JavaScript to record last touch y point. I think this is the only solution , right ? – SamFX Sep 07 '22 at 20:09
  • You mean a JavaFX input field or an html input embedded in the web page? If the latter, doesn’t it get scrolled by the browser itself? Or can’t you scroll manually? JS is a nice solution anyway. – José Pereda Sep 07 '22 at 21:24

2 Answers2

0

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

SamFX
  • 19
  • 6
0

Are you testing on a Samsung device?

Try applying this setting in your POM.

<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>${gluonfx.plugin.version}</version>
<configuration>
    <runtimeArgs>
        <arg>-Dmonocle.input.touchRadius=4</arg>
    </runtimeArgs>
Javateer
  • 65
  • 6
  • I was using Samsung device ,but now I am using Google Nexus device for testing . I will test this solution but in general I need a general solution that works in any android device – SamFX Sep 09 '22 at 04:29
  • I was testing on a Samsung and a Google Pixel. My touch events on mobile were only registering on the Pixel, not the Samsung. The workaround I shared with you above came from here: https://github.com/gluonhq/substrate/issues/987. However, as I explained in this provided link near its end, the workaround came with tradeoffs. Instead, I found a way to make GluonFX apps treat touch events the same across all devices without the mentioned tradeoff. I have a special Java class in my app's codebase for this. Hopefully GluonHQ will fix it so others won't have to also code around it too as I have. – Javateer Sep 09 '22 at 19:19