Qt on Android 实现App普通全屏、沉浸模式、粘性沉浸模式

说明

我的实现方法,与网上的常见的方法不同,我觉得网上常见的方法太挫了,一点都不酷,Qt的核心价值观就是酷,所以怎么能用那么挫的方法呢。

根据Qt5.7后的一个新函数,我研究出了我认为比较酷的方法。

 

普通方法

想要Qt实现App全屏,在网上一搜就有很多文章教你怎么实现。

主要过程有三步:

1.添加一个继承自QtActivity的Activity;

2.在这个Activity上添加全屏代码;

3.修改AndroidManifest.xml文件的Launch Activity为这个Activity。

不知道为什么我感觉这个方法不太好。

继承QtActivity以及修改AndroidManifest.xml总是让我感觉不太科学。

因此我一直想找到不需要这么做的方法。在看到Qt5.7版本后的一个新函数后,我终于找到了实现的新方法。

 

新方法

要实现全屏有三个要解决的点:

1.要执行Java的代码;

2.要提供当前Activity(也有一种说法叫上下文,context);

3.要在UI线程执行。

为什么要在UI线程执行呢,因为在Android系统,全部跟界面有关的操作都要在UI线程执行。

如果在非UI线程执行全屏代码,程序则会崩溃。

而Qt自5.7后,在QtAndroid类新增了一个静态函数:

void QtAndroid::runOnAndroidThread(const QtAndroid::Runnable &runnable)

这个静态函数就是关键。

 

步骤

1.写Android全屏的Java代码

Android的全屏模式共有三种——普通全屏,沉浸式全屏,粘性沉浸式全屏。

详细资料:https://developer.android.com/training/system-ui/immersive

为了后续方便,这里使用了一个单例。

JFullScreen.java (文件路径:android\src\org\qtproject\junj)

package org.qtproject.junj;

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.view.View;

public class JFullScreen {
    static final private String TAG = "JFullScreen";
    static private JFullScreen mInstance = new JFullScreen();
    private Context mContext = null;

    private JFullScreen() { }

    static public JFullScreen instance() { return mInstance; }

    public void setContext(Context context) {
        Log.d(TAG, "context: " + context.toString());
        mContext = context;
    }


    public void fullScreenLeanBack() {
        Log.d(TAG, "fullScreenLeanBack");
        Activity activity = (Activity) mContext;
        View decorView = activity.getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN);
    }

    public void fullScreenImmersive() {
        Log.d(TAG, "fullScreenImmersive");
        Activity activity = (Activity) mContext;
        View decorView = activity.getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_IMMERSIVE
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN);
    }

    public void fullScreenStickyImmersive() {
        Log.d(TAG, "fullScreenStickyImmersive");
        Activity activity = (Activity) mContext;
        View decorView = activity.getWindow().getDecorView();
        decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN);
    }

    public int addTest(int value0, int value1) {
        Log.d(TAG, "I'm a test.");
        return value0 + value1;
    }
}

2.使用JNI写调用Java代码的C++类

这里主要使用QAndroidJniObject类来实现调用Java代码,并使用QtAndroid::runOnAndroidThread让代码执行在UI线程。

关于QAndroidJniObject的使用技巧以后再讲。

仔细查看代码,可以发现Java部分之所以要用单例是有原因的,主要就是为了QtAndroid::runOnAndroidThread用起来更方便,在Runnable的实现里直接调用instance获取实例,不必每次再重新创建并传递Context。

jfullscreen.h

#ifndef JFULLSCREEN_H
#define JFULLSCREEN_H

#include 

class JFullScreen
{
public:
    JFullScreen();
    void fullScreenLeanBack();
    void fullScreenImmersive();
    void fullScreenStickyImmersive();
    int addTest(int value0, int value1);

private:
    QAndroidJniObject _javaClass;
};

#endif // JFULLSCREEN_H

jfullscreen.cpp

#include "jfullscreen.h"

#include <functional>
#include <QtAndroid>

// JFS: JFullScreen
#define JFS_CLASSNAME "org/qtproject/junj/JFullScreen"
#define JFS_SIGNTURE_INSTANCE "()Lorg/qtproject/junj/JFullScreen;"

void realFullScreenLeanBackMethod() {
    QAndroidJniObject javaCustomFunction = QAndroidJniObject::callStaticObjectMethod(JFS_CLASSNAME, "instance", JFS_SIGNTURE_INSTANCE);
    javaCustomFunction.callMethod<void>("fullScreenLeanBack", "()V");
}
void realFullScreenImmersiveMethod() {
    QAndroidJniObject javaCustomFunction = QAndroidJniObject::callStaticObjectMethod(JFS_CLASSNAME, "instance", JFS_SIGNTURE_INSTANCE);
    javaCustomFunction.callMethod<void>("fullScreenImmersive", "()V");
}
void realFullScreenStickyImmersiveMethod() {
    QAndroidJniObject javaCustomFunction = QAndroidJniObject::callStaticObjectMethod(JFS_CLASSNAME, "instance", JFS_SIGNTURE_INSTANCE);
    javaCustomFunction.callMethod<void>("fullScreenStickyImmersive", "()V");
}

JFullScreen::JFullScreen()
{
    if(!QAndroidJniObject::isClassAvailable(JFS_CLASSNAME)) {
        qDebug("JFullScreen is unavailable.");
        return;
    }

    _javaClass = QAndroidJniObject::callStaticObjectMethod(JFS_CLASSNAME, "instance", JFS_SIGNTURE_INSTANCE);

    if(!_javaClass.isValid()) {
        qDebug(" JFullScreen instance is invalid.");
    }

    _javaClass.callMethod<void>("setContext", "(Landroid/content/Context;)V", QtAndroid::androidActivity().object<jobject>());
}

//use runOnAndroidThread() in order to run on android ui thread
void JFullScreen::fullScreenLeanBack()
{
    QtAndroid::Runnable runnable = realFullScreenLeanBackMethod;
    QtAndroid::runOnAndroidThread(runnable);
}

void JFullScreen::fullScreenImmersive()
{
    QtAndroid::Runnable runnable = realFullScreenImmersiveMethod;
    QtAndroid::runOnAndroidThread(runnable);
}

void JFullScreen::fullScreenStickyImmersive()
{
    QtAndroid::Runnable runnable = realFullScreenStickyImmersiveMethod;
    QtAndroid::runOnAndroidThread(runnable);
}

int JFullScreen::addTest(int value0, int value1)
{
    jint sum = _javaClass.callMethod<jint>("addTest", "(II)I", value0, value1);
    return sum;
}

3.测试

创建一个JFullScreen进行测试。

    JFullScreen *pManager = new JFullScreen;
    int sum = pManager->addTest(1, 2);
    Q_ASSERT(sum == 3);
//    pManager->fullScreenLeanBack();
//    pManager->fullScreenImmersive();
    pManager->fullScreenStickyImmersive();

总结

这样一来,我们整个代码的结构是非常简明的,感觉很清爽,不需要动那些不应该的动的东西,并且简单的复制到任何一个项目都可以直接使用,简直不能再棒了。

通过继承QtActivity并修改AndroidManifest.xml文件来实现的都没我酷,嗯。

Github

https://github.com/WingNan/QtAndroidFullScreen

效果

贴一下粘性沉浸模式的效果。


 

 

 

你可能感兴趣的:(Android,Qt)