QML: Show Android native splash screen in your QML app

In this post a solution to make a pure QML splash screen has been proposed and could be used for Android app also. However, since Qt/QML app are "wrapped" inside pure Android code a solution like these could be a little slow especially in the first step of application startup. Here another solution specific for Android app more faster to show splash screen at startup.



Full project example can be found here

Basically there are three phases during Android QML app startup. In the first, after app launched, the native Android code is executed with native settings from AndroidManifest.xml file. Using the standard template the default theme is showed, that's mean, usually, a black screen with the app icon and name as title on the top of the screen. In the second phase Qt framework take control of the screen and start to paint application UI controls. In the third phase, the last, the app is ready to use. Splash screen is used to show something more "professional" during the first two phases. Especially during second phase where, if the app have a complex interface or need time to make some initialization steps, is possible to have the largest delay. Now since these two phases are not directly connected we have to provide two different solutions showing same splash screen to give "impression" the app is loading from start to ready status.

At first we have to build the layer to use for make the splash screen for both phases. Follow the content of splashscreen.xml. As you can see there is the background color and the app icon on the center. Unfortunately, using this way, is not possible to add text in string format. If you want to add text you have to provide it in image format.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle" >
            <solid android:color="#81DAF5" />
        </shape>
    </item>
    <item>
         <bitmap android:src="@drawable/icon" android:gravity="center" />
    </item>
</layer-list>

 Result will be the following:


Now the first phase. Basically you have to provide a custom theme for your app instead of the default one. Follow the content of the file splashscreentheme.xml. The trick here is to provide the splash screen layer as windows background. Pay attention since in some similar example you can find around the tag to show the layer is android:windowBackground instead of android:background used in this example. The tag android:windowBackground have a problem that use the full screen area ignoring the space occupied by the Android status bar, usually, on top. This "full" size is used for calculate the center position of the icon. On the contrary, after moved to the second phase, the Qt window use only the available screen space excluding the space occupied by the status bar. This have as a practical consequence that from the first to the second phase you can see the central icon to move down of some pixel, not very beautiful to see. Using android:background tag will fix this problem since take in consideration only the available space as Qt do.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="SplashScreenTheme" parent="android:Theme">
        <item name="android:background">@drawable/splashscreen</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">false</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>
</resources>

For set the custom theme for you Android app you have to add the following tag into the AndroidManifest.xml

<activity .... android:theme="@style/SplashScreenTheme">

This will ensure the splash screen is showed immediately after the app is launched. Now the second phase. If you note on standard AndroidManifest.xml generated by Qt Creator there are some commented lines regarding splash screen as follow:

<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/splashscreen"/>
<meta-data android:name="android.app.splash_screen_sticky" android:value="true"/>

Again, remember these lines are commented by default (and then ignored by the Android compiler), you have to uncomment them to take effect (plase, note in the default version the android:resource value is @drawable/logo but in this example has been changed to @drawable/splashscreen for use our theme). The first line instruct Qt to show the splash screen layer during the framework initialization phase. This point is very important and is connected to the second line. Indeed the second line is a part of a new feature introduced from Qt version 5.8. Basically Qt show by default the plash screen provided only during internal initialization phase and remove it after the main code start to paint controls on window. For QML app this mean you can see a short phase (depending of the hardware speed and the complexity of your app) when the QML engine paint the control on the screen. As you can understand this is not a good view and for this reasons the new feature has been introduced. Set to true the android.app.splash_screen_sticky option instruct the Qt framework to don't remove the splash screen. Command to remove the splash screen is in charge of the main app after all the UI paint is finished. On the main code you have to call the function in red after the QML engine finished to load all the components and immediately before the main app loop will start:

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    QtAndroid::hideSplashScreen();

    return app.exec();
}

This will ensure the UI will be ready to use once splash screen disappear. This feature work quite well, there is only a small flickers showing in all Android version under 5.0 but is not a big problem at all.

Comments

  1. Thank you very much. I has this problem exactly and your post helped me to resolve it. Let me tell you that I resolved the small flick you mentioned in the last sentence, and now I have a smooth transition from splash to QT app. I resolved it as following,

    1- I have a class called "receiver", I declared in "receiver.h" a slot as

    public slots:
    void hideSplash();

    2- In "receiver.cpp",
    void Receiver::hideSplash() {
    #ifdef Q_OS_ANDROID
    QtAndroid::hideSplashScreen();
    #endif
    }

    3- From QML side, and after defining a connection to "receiver" class, and after the first app's page has been loaded, I invoked the defined slot through the following signal,

    Component.onCompleted: {
    receiver.hideSplash();
    }

    In this flow, the splash will remain until the first QML page fully loaded producing smooth transition.

    ReplyDelete
    Replies
    1. Hi
      Flick I referring happen between the first phase, when Android load the app, and the second phase when Qt framework start to prepare the window by paint the controls. Both phases are before the hideSplashScreen() function call and he flicker seem an issue related to older Android versions. However is not a big problem at least for me but if you found a solution you like more happy to know that. ^_^

      Delete

Post a Comment

Popular posts from this blog

Access GPIO from Linux user space

Launch an app from Android shell terminal