我的实现方法,与网上的常见的方法不同,我觉得网上常见的方法太挫了,一点都不酷,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)
这个静态函数就是关键。
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;
}
}
这里主要使用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;
}
创建一个JFullScreen进行测试。
JFullScreen *pManager = new JFullScreen;
int sum = pManager->addTest(1, 2);
Q_ASSERT(sum == 3);
// pManager->fullScreenLeanBack();
// pManager->fullScreenImmersive();
pManager->fullScreenStickyImmersive();
这样一来,我们整个代码的结构是非常简明的,感觉很清爽,不需要动那些不应该的动的东西,并且简单的复制到任何一个项目都可以直接使用,简直不能再棒了。
通过继承QtActivity并修改AndroidManifest.xml文件来实现的都没我酷,嗯。
https://github.com/WingNan/QtAndroidFullScreen
贴一下粘性沉浸模式的效果。