Здравствуйте! Уверен, многие слышали о том, что Qt очень хорош для кросплатформенной разработки мобильных приложений. Однако, для решения некоторых задач приходится иметь дело с нативным кодом (Java, Objective-C), к примеру, вызов камеры, галереи, вызов стороннего api.
В этой статье на простом примере задания прозрачности для status bar я покажу, как осуществляется вызов нативного кода Java и Objective-C.
Andoid
Возможность использования прозрачного status bar появилась в Android 4.4 KitKat. Для того, чтобы status bar стал прозрачным, необходимо в Activity нашего проекта указать флаг прозрачности для Window (не путать с QQuickWindow, который используется для отображения QML).
Открываем вкладку Проекты → Добавить сборку под Andoid → “Сборка” → Нажимаем “Подробнее” в “Собрать Android APK” → “Создать шаблоны”.
Тем самым мы создали AndroidManifest, папку с ресурсами и файлы gradle, которые будут находится в папке android. Для размещения нашего java класса создадим папку src в папке android.
Создадим файл MyActivity.java. Важно, чтобы путь до файла совпадал с именем пакета, т.е. используя пакет с именем com.example.myPackage, путь должен быть android/src/com/example/myPackage/MyActivity.java
package com.example.myPackage;
import org.qtproject.qt5.android.bindings.QtActivity;
import android.app.Activity;
import android.os.Bundle;
public class MyActivity extends QtActivity
{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Теперь, нам надо задать имя Activity в AndroidManifest.xml. Ищем
android:name="com.example.myPackage.MyActivity"
и меняем на
android:name="org.qtproject.qt5.android.bindings.QtActivity"
Этими нехитрыми манипуляциями мы переопределили стандартную QtActivity.
Функция выставляющая флаг прозрачности status bar и пример её применения:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTranparentStatusBar();
}
void function setTranparentStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
getWindow().setStatusBarColor(Color.TRANSPARENT);
} else {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
Функция, возвращающая высоту status bar:
public int statusBarHeight() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return 0;
}
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
Для того, чтобы вызвать метод Java класса из QML потребуется написать С++ класс, который будет использовать JNI. Для работы с JNI добавим в наш *.pro файл модуль Android:
QT += androidextras.
Создадим singleton класс DeviceInfo.
DeviceInfo.h:
#pragma once
#include <QObject>
class DeviceInfo : public QObject
{
Q_OBJECT
Q_PROPERTY(int statusBarHeight READ statusBarHeight)
public:
DeviceInfo(QObject *parent = NULL);
static DeviceInfo &instance(QObject *parent = 0);
Q_INVOKABLE int statusBarHeight();
private:
static DeviceInfo _instance;
};
DeviceInfo.cpp:
#include "DeviceInfo.h"
#if defined(Q_OS_ANDROID)
#include <QAndroidJniObject>
#include <QtAndroidExtras>
#include <QtAndroid>
#endif
DeviceInfo::DeviceInfo(QObject *parent)
: QObject(parent)
{}
DeviceInfo &DeviceInfo::instance(QObject *parent)
{
static DeviceInfo instance(parent);
return instance;
}
int DeviceInfo::statusBarHeight()
{
#if defined (Q_OS_ANDROID)
QAndroidJniObject activity = QtAndroid::androidActivity();
jint height = activity.callMethod<jint>("statusBarHeight");
return (int) height;
#endif
return 0;
}
Далее, определим наш класс в QML:
view.rootContext()->setContextProperty("DeviceInfo", &DeviceInfo::instance());
Всё готово, осталось лишь вызвать метод statusBarHeight в QML:
Rectangle {
width: parent.width
height: DeviceInfo.statusBarHeight()
}
Итог на экране:
iOS
Возможность задания различного стиля у status bar в iOS появилась в iOS 7.0. Для того, чтобы status bar в нашем приложение был прозрачен, нам нужно сделать 3 вещи:
- Изменить info.plist, а именно, изменить ключ UIViewControllerBasedStatusBarAppearance:
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
- Для отображения QQuickView или QQuickWindow использовать метод showFullScreen() вместо show().
- Выставить у status bar стиль UIStatusBarStyleLightContent
Если с первыми двумя пунктами всё понятно, разберём третий более подробно. Изменить стиль у status bar'a можно следующим методом на Objective-C:
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
Функция, возвращающая высоту status bar’a:
[UIApplication sharedApplication].statusBarFrame.size.height
Для того, чтобы код на Objective-C работал в классе DeviceInfo, нам потребуется поменять разрешение исходника с .cpp на .mm. Поэтому в .pro файле сделаем следующее:
HEADERS += \
Include/DeviceInfo.h
!ios {
SOURCES += \
Source/DeviceInfo.cpp
}
ios {
OBJECTIVE_SOURCES += \
Source/DeviceInfo.mm
}
DeviceInfo.mm:
#include "DeviceInfo.h"
#import <UIKit/UIKit.h>
DeviceInfo::DeviceInfo(QObject *parent)
: QObject(parent)
{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
DeviceInfo &DeviceInfo::instance(QObject *parent)
{
static DeviceInfo instance(parent);
return instance;
}
int DeviceInfo::statusBarHeight()
{
return [UIApplication sharedApplication].statusBarFrame.size.height;
}
Итог на экране:
Заключение
Я постарался осветить как можно подробнее каждый шаг, чтобы на основе примеров из статьи вы могли легко дополнить свой мобильный проект нативным кодом. Исходный код примера смотрите на GitHub.