When you hide the SoftKeyboard
via the Android Back Button
, touching the (still focused) inputNode won't make the keyboard show again.
To solve this issue I'm using the following class:
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);
}
});
}
}
While this is working, it seems like an ugly workaround. How could this be done without using JDK internal classes (TextFieldSkin
, HitInfo
)?
EDIT: here is another solution, based on José Pereda's answer:
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);
}
}
}