作为一名工作中使用Qt的开发者,主要做的是桌面应用开发。开发Android应用主要是想要尝试移动端开发一些简单、练习性质的应用。使用Qt Quick开发时只是大致看了Android官方文档的基础内容部分,并没有足够的Android知识储备。所以在这个过程中可能会遇到比较多的问题。
为了避免自己以后重复遇到类似问题,或者你也正遇到类似问题而正在网络上到处搜索和尝试解决方案,我在此做个记录分享给有需要的各位。
以后遇到凡涉及Qt Android开发的相关问题,都会在这里更新。
整体实现过程是:
为了便于演示,我先建立了一个示例Qt Quick工程:
创建Android工程模板文件:
在文件浏览器中找到 AndroidManifest.xml
文件,然后使用其他编辑器打开:
找到
标签的android:name
属性,其默认值为:org.qtproject.qt5.android.bindings.QtActivity
。
Activity
类是android中的主要组件之一,用来显示界面。在AndroidManifest.xml
中定义的
就表示了一个该组件。其android:name
属性则指向了派生自Activity
类的子类。所以org.qtproject.qt5.android.bindings.QtActivity
就表示这是Qt中实现的Activity
子类。它实现了加载和显示Qt Quick绘制界面的组件等功能。
在原生Android开发中,不同页面会定义不同的Activity
。但使用Qt Quick, Flutter等采用Direct UI方式实现的第三方开发框架则只定义了一个Activity
。里面不同页面实际都是使用OpenGL等直接绘制的。
回到正题,我们需要创建新的java源文件,然后自己定义一个 activity,但它不是继承Activiy
,而是继承org.qtproject.qt5.android.bindings.QtActivity
:
然后将android:name
属性指向这个类:
最后的重点来了,我们需要在自定义的 activity启动时设置相应属性,并获取到状态栏高度:
// me/quick/app/SameStatusBarActivity.java
package me.quick.app;
import android.content.Context;
import android.os.Bundle;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.app.ActionBar;
import android.app.Application;
import android.content.res.Resources;
public class SameStatusBarActivity extends org.qtproject.qt5.android.bindings.QtActivity {
public SameStatusBarActivity() { }
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21) {
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
decorView.setSystemUiVisibility(option);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//透明导航栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
// getWindow().getDecorView().setFitsSystemWindows(true);
}
// ActionBar actionBar = getActionBar();
// actionBar.hide();
// 获取并保存状态栏高度
int resourceId = getResources().getIdentifier("status_bar_height", "dimen","android");
if (resourceId > 0) {
statusHeight = getResources().getDimensionPixelSize(resourceId);
} else {
statusHeight = 48;
}
}
private static int statusHeight;
// 该接口在Qt C++中调用
public static int getStatusBarHeight() {
return statusHeight;
}
}
然后在Qt中定义一个新的继承自QObject
的类,我将其取名为AndroidHelper
:
#include
class AndroidHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(int androidStatusBarHeight READ androidStatusBarHeight)
public:
explicit AndroidHelper(QObject *parent = nullptr);
int androidStatusBarHeight();
private:
int mAStatusBarHeight {0};
};
我们在AndroidHelper::androidStatusBarHeight
调用之前定义的java类SameStatusBarActivity
的类静态方法getStatusBarHeight
:
int AndroidHelper::androidStatusBarHeight()
{
if (mAStatusBarHeight == 0) {
mAStatusBarHeight = QAndroidJniObject::callStaticMethod(
"me/quick/app/SameStatusBarActivity",
"getStatusBarHeight"
);
}
return mAStatusBarHeight;
}
在main.cpp
文件中,我们创建一个AndroidHelper
对象,并将其设置为属性,以便在QML中使用:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// **
AndroidHelper *helper = new AndroidHelper(&engine);
engine.rootContext()->setContextProperty("AndroidHelper", helper);
// **
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
最后就可以在QML中调用AndroidHelper.androidStatusBarHeight
获取状态栏高度了。
这里,我们在QML中定义一个ToolBar,它将包括Android StatusBar的部分,使其背景和应用ToolBar保持一致。
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Material 2.12
ApplicationWindow {
id: _win
visible: true
width: 640
height: 480
header: ToolBar {
ColumnLayout {
anchors.fill: parent
spacing: 0
Rectangle {
Layout.fillWidth: true
height: AndroidHelper.androidStatusBarHeight * 0.35
color: Material.primary
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
ToolButton {
text: 'Ok'
onClicked: _drawer.open()
}
}
}
}
Text {
anchors.centerIn: parent
font.bold: true
font.pointSize: 48
text: AndroidHelper.androidStatusBarHeight
}
Drawer {
id: _drawer
edge: Qt.LeftEdge
width: _win.width * 0.7
height: _win.height
Pane {
anchors.fill: parent
Text {
anchors.centerIn: parent
text: 'Drawer'
font.bold: true
font.pointSize: 48
}
}
}
}
在上面的QML代码中,我为header
属性定义了ToolBar
组件,内部使用ColumnLayout
布局,第一个子组件使用了一个Rectangle
,让其作为Android StatusBar的背景区域。
为了使应用使用Google Material界面风格,还需要定义一个qtquickcontrols2.conf
文件:
[Controls]
Style=Material
[Material]
Theme=Light
然后在QML将StatusBar区域的Rectangle
颜色设置为Material.primary
,这和ToolBar
的颜色一致。
此处使用的方法,存在一个问题:从android java中获取的StatusBar高度值,不能直接在QML中作为高度值设置,需要乘个系数去和真实高度做大致匹配。在不同设备中这个系数也不一定一致。