QML: Resize controls when Android virtual keyboard come up
QML have currently a bug (current version 5.8 still have) in resize window when android keyboard come up. It happen when virtual keyboard is requested the QML window show a bad flicker by repaint the top control on the center of the screen and back on top again. This look really unprofessional.
Full project example can be found here
In await of Qt team to fix the bug will show here a small workaround for allow the QML window to be correctly resized when android virtual keyboard come up over the window. The solution consist in detect when virtual keyboard is showed, calculate the height and resize our window control based to the "new" screen space available (decreased). Qt framework already export a method to get virtual keyboard size using QInputMethod::keyboardRectangle() but the documentation cleary write this doesn't work with floating keyboard like android virtual keyboard, this mean we have to find another way. Solution proposed here will consist in use mixed C++ and native java code to install a keyboard listener informing about new available screen size when keyboard is present. At first you have to add the followinf tag to the AndroidManifest.xml app file:
<activity .... android:windowSoftInputMode="adjustPan">
This will instruct android keyboard to don't try to resize the window but just show keyboard below the current window. Once done this is the java native code to add to your project:
public class VirtualKeyboardListener { private static Activity m_ActivityInstance; public static void Init(Activity ActivityInstance) { m_ActivityInstance = ActivityInstance; } public static native void VirtualKeyboardStateChanged(int VirtualKeyboardHeight); public static void InstallKeyboardListener() { final View AppRootView = ((ViewGroup)m_ActivityInstance.findViewById(android.R.id.content)).getChildAt(0); AppRootView.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int ScreenHeight, VirtualKeyboardHeight; Rect WindowFrameRect = new Rect(); Rect ContentFrameRect = new Rect(); m_ActivityInstance.getWindow().getDecorView().getWindowVisibleDisplayFrame(WindowFrameRect); AppRootView.getWindowVisibleDisplayFrame(ContentFrameRect); ScreenHeight = AppRootView.getRootView().getHeight(); VirtualKeyboardHeight = (ScreenHeight - (ContentFrameRect.bottom - ContentFrameRect.top) - WindowFrameRect.top); if(VirtualKeyboardHeight < 100) VirtualKeyboardHeight = 0; VirtualKeyboardStateChanged(VirtualKeyboardHeight); } } ); } }
At app startup is necessary to call the function for install the virtual keyboard listener and register the callback JNI function from C++ side that will be called by the listener when virtual keyboard show up and passing as param the keyboard height. This same C++ function will pass the parameter to QML code for allow correct resize:
QObject *pQmlRootObject = NULL; void AndroidVirtualKeyboardStateChanged(JNIEnv *env, jobject thiz, jint VirtualKeyboardHeight) { Q_UNUSED(env) Q_UNUSED(thiz) if(pQmlRootObject != NULL) { QMetaObject::invokeMethod(pQmlRootObject, "androidVirtualKeyboardStateChanged", Qt::AutoConnection, Q_ARG(QVariant, VirtualKeyboardHeight) ); } } void InitializeForAndroid() { JNINativeMethod methods[] = { { "VirtualKeyboardStateChanged", "(I)V", reinterpret_cast(AndroidVirtualKeyboardStateChanged) } }; QAndroidJniObject javaClass("com/falsinsoft/example/keyboardsize/VirtualKeyboardListener"); QAndroidJniEnvironment env; jclass objectClass = env->GetObjectClass(javaClass.object ()); env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0])); env->DeleteLocalRef(objectClass); QAndroidJniObject::callStaticMethod ("com/falsinsoft/example/keyboardsize/VirtualKeyboardListener", "InstallKeyboardListener"); } int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QLatin1String("qrc:/main.qml"))); pQmlRootObject = engine.rootObjects().at(0); InitializeForAndroid(); return app.exec(); }
Please note you can not resize the QML main window since android OS need the main window to be full screen. You need to resize the child controls inside the window to "compensate" the part of the screen currently covered by the virtual keyboard.
function androidVirtualKeyboardStateChanged(virtualKeyboardHeight) { if(virtualKeyboardHeight > 0) appBody.height = (appBody.parent.height - (virtualKeyboardHeight / Screen.devicePixelRatio)); else appBody.height = appBody.parent.height; }
Virtual keyboard size returned is in pixel but you need to calculate the space to "free" also considering the currect screen DPI as showed in the code above.
Comments
Post a Comment