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 threadvoid 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
效果
贴一下粘性沉浸模式的效果。
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- (转摘)Android腾讯微博客户端开发四:微博发送篇(QQ表情,@搜索)
- Android(安卓)App调用SDK 登录第一次总是失败的解决方法
- Android(安卓)弹无虚发之第四弹:你应该掌握的Notification(Notific
- 进程(一) 1.2 Android中异步处理大杀器——Handler(1)
- [置顶] android 程序开发的插件化 模块化方法 之二
- [置顶] Android(安卓)轻松实现网络交互模板
- 【Android(安卓)界面效果17】Android手机平板两不误,使用Fragment
- Android(安卓)IPC【Inter-Process Communication】机制二【Andro