Здравствуйте! Уверен, многие слышали о том, что 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.
