如何让SoftKeyboard再次出现

时间:2017-01-07 16:37:59

标签: gluon-mobile

当您通过SoftKeyboard隐藏Android Back Button时,触摸(仍然聚焦的)inputNode将不会再次显示键盘。 要解决这个问题,我正在使用以下类:

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 {
                fakeFocusTarget.requestFocus();
                requestFocus();

                HitInfo hitInfo = ((TextFieldSkin) getSkin()).getIndex(e.getX(), e.getY());
                ((TextFieldSkin) getSkin()).positionCaret(hitInfo, false);
            }
        });
    }
}

虽然这有效但似乎是一个丑陋的解决方法。如果不使用JDK内部类(TextFieldSkinHitInfo)?

,怎么办呢?

编辑:这是另一种解决方案,基于JoséPereda的回答:

public class RefocusableTextField extends TextField {

    private Optional<KeyboardService> service;

    public RefocusableTextField(String text) {
        this();
        setText(text);
    }

    public RefocusableTextField() {
        service = Services.get(KeyboardService.class);

        addEventFilter(MouseEvent.MOUSE_PRESSED, event ->
        {
            if (!isFocused()) {
                event.consume();
            }
        });

        addEventHandler(MouseEvent.MOUSE_CLICKED, e ->
        {
            if (!isFocused()) {
                requestFocus();
                end();

            } else {
                service.ifPresent(KeyboardService::show);
            }
        });
    }
}


public class AndroidKeyboardService implements KeyboardService {

    private static final float       SCALE  = FXActivity.getInstance().getResources().getDisplayMetrics().density;

    private final InputMethodManager imm;

    private Rect                     currentBounds;
    private DoubleProperty           visibleHeight;

    private OnGlobalLayoutListener   layoutListener;

    private boolean                  keyboardVisible;

    public AndroidKeyboardService() {
        imm = (InputMethodManager) FXActivity.getInstance().getSystemService(FXActivity.INPUT_METHOD_SERVICE);
        initLayoutListener();
    }

    private void initLayoutListener() {
        double screenHeight = MobileApplication.getInstance().getScreenHeight();
        currentBounds = new Rect();    
        visibleHeight = new SimpleDoubleProperty(screenHeight);
        visibleHeight.addListener((ov, n, n1) -> onHeightChanged(n, n1));


        layoutListener = layoutListener(visibleHeight);

        FXActivity.getViewGroup().getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
        Services.get(LifecycleService.class).ifPresent(l ->
        {
            l.addListener(LifecycleEvent.RESUME, () -> FXActivity.getViewGroup().getViewTreeObserver().addOnGlobalLayoutListener(layoutListener));
            l.addListener(LifecycleEvent.PAUSE, () -> FXActivity.getViewGroup().getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener));
       });
    }

    private OnGlobalLayoutListener layoutListener(DoubleProperty height) {
        return () -> height.set(getCurrentHeigt());
    }

    private float getCurrentHeigt() {
        FXActivity.getViewGroup().getRootView().getWindowVisibleDisplayFrame(currentBounds);
        return currentBounds.height() / SCALE;
    }

    private void onHeightChanged(Number oldHeight, Number newHeight) {
        double heightDelta = newHeight.doubleValue() - oldHeight.doubleValue();
        keyboardVisible = heightDelta < 0;
    }

    @Override
    public boolean isKeyboardVisible() {
        return keyboardVisible;
    }

    @Override
    public void show() {
        if (!keyboardVisible) {
            imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
        }
    }

    @Override
    public void hide() {
        if (keyboardVisible) {
            imm.toggleSoftInput(0, InputMethodManager.HIDE_IMPLICIT_ONLY);
        }
    }
}

2 个答案:

答案 0 :(得分:1)

如您所知,Android的JavaFX层管理软键盘,它实际上仅由焦点获得/丢失event触发。

所以你的方法是正确的,但如果你想避免使用私有API,我会看到两种可能的解决方案:

  • 转到JavaFX层,修改它并构建它......它可以完成,但它需要做很多工作,它将在下一版JavaFXPorts版本中破解。

  • 创建自定义插件并提供API以便在您方便时管理软键盘。

对于第二个选项,使用新的Down插件API非常容易。在您的主程序包上创建包com.gluonhq.charm.down.plugins这两个类:

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

}

现在在Android软件包下,将此类添加到包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);
        }
    }

}

现在,从您的代码中,您可以轻松地从文本字段中调用键盘。

我已根据此implementation添加了long-press类型的事件:

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

因此,如果您有一个TextField,并且想要在用户按住并暂停一段时间时调用键盘,您只需要:

TextField textField = new TextField();

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

修改的 注意:我已根据此answer添加了键盘的可见性控制。

额外提示:按照这种方法,您只需一步即可在长按时提供触觉反馈......

答案 1 :(得分:0)

我们通过为KeyEvents添加侦听器并在Android和iOS上成功测试它来改进了您的初始版本。

可以在视图开始时轻松调用defocus方法,这样它就不会自动将第一个文本字段聚焦于&#34; Platform.runlater(() - &gt; textField.defocus() );&#34;

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