How to make the SoftKeyboard show up again

后端 未结 2 1681
半阙折子戏
半阙折子戏 2020-12-21 15:16

When you hide the SoftKeyboard via the Android Back Button, touching the (still focused) inputNode won\'t make the keyboard show again. To solve th

相关标签:
2条回答
  • 2020-12-21 15:28

    As you know, the JavaFX layer for Android manages the soft keyboard, and it's actually triggered only by the focus gained/lost event.

    So your approach is correct, but if you want to avoid private API, I see two possible solutions:

    • Go to the JavaFX layer, modify it and build it... It can be done, but it is a lot of work, and it will break on the next release of the JavaFXPorts version.

    • Create a custom plugin and provide API to manage the soft keyboard at your convenience.

    For the second option, this is very easy to do with the new Down plugin API. On your main package create under the package com.gluonhq.charm.down.plugins these two classes:

    KeyboardService

    package com.gluonhq.charm.down.plugins;
    
    public interface KeyboardService {
        public void show();
        public void hide();
    }
    

    KeyboardServiceFactory

    package com.gluonhq.charm.down.plugins;
    
    import com.gluonhq.charm.down.DefaultServiceFactory;
    
    public class KeyboardServiceFactory extends DefaultServiceFactory<KeyboardService> {
    
        public KeyboardServiceFactory() {
            super(KeyboardService.class);
        }
    
    }
    

    And now under the Android package, add this class under the package com.gluonhq.charm.down.plugins.android:

    AndroidKeyboardService

    package com.gluonhq.charm.down.plugins.android;
    
    import android.view.inputmethod.InputMethodManager;
    import com.gluonhq.charm.down.plugins.KeyboardService;
    import javafxports.android.FXActivity;
    
    public class AndroidKeyboardService implements KeyboardService {
    
        private final InputMethodManager imm;
    
        private boolean visible = false;
    
        public AndroidKeyboardService() {
            imm = (InputMethodManager) FXActivity.getInstance().getSystemService(FXActivity.INPUT_METHOD_SERVICE);
    
            final ViewTreeObserver.OnGlobalLayoutListener listener = () -> {
                Rect rect = new Rect();
                FXActivity.getViewGroup().getWindowVisibleDisplayFrame(rect);
                int heightDiff = FXActivity.getViewGroup().getRootView().getHeight() - rect.height();
                visible = (heightDiff > FXActivity.getViewGroup().getRootView().getHeight() / 4);
            };
    
            Services.get(LifecycleService.class).ifPresent(l -> {
                l.addListener(LifecycleEvent.RESUME, () -> 
                        FXActivity.getViewGroup().getViewTreeObserver().addOnGlobalLayoutListener(listener));
                l.addListener(LifecycleEvent.PAUSE, () -> 
                        FXActivity.getViewGroup().getViewTreeObserver().removeOnGlobalLayoutListener(listener));
            });
            FXActivity.getViewGroup().getViewTreeObserver().addOnGlobalLayoutListener(listener))
        }
    
        @Override
        public void show() {
            if (!visible) {
                imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
            }
        }
    
        @Override
        public void hide() {
            if (visible) {
                imm.toggleSoftInput(0, InputMethodManager.HIDE_IMPLICIT_ONLY);
            }
        }
    
    }
    

    Now from your code, you can easily call the keyboard from your textfield.

    I've added a long-press type of event, based on this implementation:

    private void addPressAndHoldHandler(Node node, Duration holdTime, EventHandler<MouseEvent> handler) {
        class Wrapper<T> { 
            T content; 
        }
    
        Wrapper<MouseEvent> eventWrapper = new Wrapper<>();
    
        PauseTransition holdTimer = new PauseTransition(holdTime);
        holdTimer.setOnFinished(event -> handler.handle(eventWrapper.content));
    
        node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
            eventWrapper.content = event;
            holdTimer.playFromStart();
        });
        node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> holdTimer.stop());
        node.addEventHandler(MouseEvent.DRAG_DETECTED, event -> holdTimer.stop());
    }
    

    so in case you have a TextField and want to call the keyboard when the user presses and holds on it for a while, all you need is:

    TextField textField = new TextField();
    
    addPressAndHoldHandler(textField, Duration.seconds(1), event -> 
        Services.get(KeyboardService.class)
             .ifPresent(KeyboardService::show));
    

    Edit Note: I have added visibility control of the keyboard, based on this answer.

    Extra Hint: Following this approach you are just one step away of providing haptic feedback on that long press...

    0 讨论(0)
  • 2020-12-21 15:32

    We improved the initial version of yours with adding a listener for KeyEvents and tested it successfully on Android and iOS.

    The defocus-method can easily be called at the start of a view, so that it won't automatically focus the first textfield with "Platform.runlater(() -> textField.defocus());".

    public class RefocusableTextField extends TextField {
    
      private Region fakeFocusTarget;
    
      public RefocusableTextField(String text) {
        this();
        setText(text);
      }
    
      public RefocusableTextField() {
        fakeFocusTarget = new Region();
        fakeFocusTarget.setManaged(false);
        getChildren().add(fakeFocusTarget);
    
        addEventFilter(MouseEvent.MOUSE_PRESSED, MouseEvent::consume);
    
        addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
          if (!isFocused()) {
            requestFocus();
          } else {
            defocus();
          }
        });
    
        addEventHandler(KeyEvent.KEY_PRESSED, e -> {
          System.out.println(e.getCode());
          if (e.getCode().equals(KeyCode.ENTER)) {
            defocus();
          }
        });
      }
    
      public void defocus() {
        fakeFocusTarget.requestFocus();
      }
    }
    
    0 讨论(0)
提交回复
热议问题