Qt Android开发问题汇总

Qt Android开发问题汇总

  • 摘要
  • 1.Qt Quick编写Android应用,使状态栏颜色和应用保持一致
    • 总结

摘要

作为一名工作中使用Qt的开发者,主要做的是桌面应用开发。开发Android应用主要是想要尝试移动端开发一些简单、练习性质的应用。使用Qt Quick开发时只是大致看了Android官方文档的基础内容部分,并没有足够的Android知识储备。所以在这个过程中可能会遇到比较多的问题。

为了避免自己以后重复遇到类似问题,或者你也正遇到类似问题而正在网络上到处搜索和尝试解决方案,我在此做个记录分享给有需要的各位。

以后遇到凡涉及Qt Android开发的相关问题,都会在这里更新。

1.Qt Quick编写Android应用,使状态栏颜色和应用保持一致

整体实现过程是:

  • 启动时在Android Java中设置状态栏为透明、应用全屏显示
  • Android端获取并存储状态栏高度
  • Qt端根据需要设置状态栏位置处QML组件的颜色

为了便于演示,我先建立了一个示例Qt Quick工程:
Qt Android开发问题汇总_第1张图片
创建Android工程模板文件:
Qt Android开发问题汇总_第2张图片

在文件浏览器中找到 AndroidManifest.xml 文件,然后使用其他编辑器打开:
Qt Android开发问题汇总_第3张图片
找到标签的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
Qt Android开发问题汇总_第4张图片
然后将android:name属性指向这个类:
Qt Android开发问题汇总_第5张图片
最后的重点来了,我们需要在自定义的 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的颜色一致。

最终效果如下:
Qt Android开发问题汇总_第6张图片Qt Android开发问题汇总_第7张图片

总结

此处使用的方法,存在一个问题:从android java中获取的StatusBar高度值,不能直接在QML中作为高度值设置,需要乘个系数去和真实高度做大致匹配。在不同设备中这个系数也不一定一致。

你可能感兴趣的:(Qt)