当您通过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内部类(TextFieldSkin
,HitInfo
)?
编辑:这是另一种解决方案,基于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);
}
}
}
答案 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();
}
}