Reputation: 6952
As the center node of a gluon view I have a scrollpane which contains several textfields in a vbox. When one of these textfields becomes the focusowner and the keyboard shows up, the textfield doesn't get repositioned according to the layout of the keyboard, so it is left covered by the keyboard. I tried putting
android:windowSoftInputMode="adjustResize"
in the AndroidManifest, but without any success.
As a workaround I translate the y-coordinates of the covered textfield to the visible area. When you press the android back button to hide the keyboard, the textfields position will be reset to its original state. The issue I'm getting here is that I don't get an event for the android back button, no matter where I add the listener:
view.addEventFilter(MobileEvent.BACK_BUTTON_PRESSED, evt -> eventFilter);
MobileApplication.getInstance().getGlassPane().addEventFilter(MobileEvent.BACK_BUTTON_PRESSED, evt -> eventFilter);
Is there any possibility to handle the positioning of a node under the keyboard, or to get a reference to the keyboard itself?
Upvotes: 1
Views: 599
Reputation: 6952
This is the solution I could come up with so far:
public class PositionAdjuster {
public static void main(String[] args) { launch(args); }
private static final float SCALE = FXActivity.getInstance().getResources().getDisplayMetrics().density;
private Node nodeToAdjust;
private ObservableValue<Node> focusOwner;
private ViewGroup viewGroup;
private Rect currentBounds;
private DoubleProperty height;
private OnGlobalLayoutListener layoutListener;
public PositionAdjuster(Node nodeToAdjust, ObservableValue<Node> focusOwner) {
this.nodeToAdjust = nodeToAdjust;
this.focusOwner = focusOwner;
initLayoutListener();
}
private void initLayoutListener() {
double screenHeight = MobileApplication.getInstance().getScreenHeight();
height = new SimpleDoubleProperty(screenHeight);
height.addListener((ov, n, n1) -> onHeightChanged(n, n1));
layoutListener = () -> height.set(getCurrentHeigt());
viewGroup = FXActivity.getViewGroup();
viewGroup.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
currentBounds = new Rect();
}
private float getCurrentHeigt() {
viewGroup.getRootView().getWindowVisibleDisplayFrame(currentBounds);
return currentBounds.height() / SCALE;
}
private void onHeightChanged(Number oldValue, Number newValue) {
double heightDelta = newValue.doubleValue() - oldValue.doubleValue();
if (heightDelta < 0) {
double maxY = getBoundsInScene(nodeToAdjust)).getMaxY();
double currentMaxY = heightDelta + maxY;
double result = currentMaxY- getBoundsInScene(focuseOwner.getValue()).getMaxY();
if (result < 0) {
nodeToAdjust.setTranslateY(result);
}
} else if (heightDelta > 0) {
nodeToAdjust.setTranslateY(0);
}
}
private Bounds getBoundsInScene(Node node) {
return node.localToScene(node.getBoundsInLocal());
}
public void removeListener() {
viewGroup.getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener);
}
}
EDIT:
I think this is a more straightforward approach. The previous version was dependent on the maxY of noteToAdjust
to be equal to the height of the screen, not taking into account e.g. the presence of a bottomBar. Now the maxY position of the focusedNode
is validated against the visible screen height, and the difference is used to reposition its parent.
public AndroidPositionAdjuster(Node parent, ObservableValue<Node> focusOwner) {
this.parent = parent;
this.focusOwner = focusOwner;
initLayoutListener();
}
private void onHeightChanged(Number oldHeight, Number newHeight) {
double heightDelta = newHeight.doubleValue() - oldHeight.doubleValue();
if (heightDelta < 0) {
double maxY = newHeight.doubleValue();
double focusedNodeY = getBoundsInScene(focusOwner.getValue()).getMaxY();
double result = maxY - focusedNodeY;
if (result < 0) {
parent.setTranslateY(result);
}
} else if (heightDelta > 0) {
parent.setTranslateY(0);
}
}
Upvotes: 1
Reputation: 313
Only layers get the MobileEvent.BACK_BUTTON_PRESSED event. One solution is to go native and use the Android API.
Upvotes: 1