Нативний код Android і iOS в Qt на прикладі status bar

Привіт! Впевнений, багато чули про те, що Qt дуже хороший для кросплатформенной розробки мобільних додатків. Однак, для рішення деяких задач доводиться мати справу з нативним кодом Java, Objective-C), наприклад, виклик камери, галереї, виклик стороннього api.
У цій статті на прикладі завдання прозорості для status bar я покажу, як здійснюється виклик нативного коду Java і Objective-C.
вжух
Andoid
Можливість використання прозорого status bar з'явилася в Чоловічий 4.4 KitKat. Для того, щоб status bar став прозорим, необхідно в Activity нашого проекту вказати прапор прозорості для Window (не плутати з QQuickWindow, який використовується для відображення QML).
Якщо хто не знає, як змінити свою Activity від QtActivityВідкриваємо вкладку Проекти → Додати збірку під 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()
}

Підсумок на екрані:
android screenshot
iOS
Можливість завдання різного стилю у status bar iOS з'явилася в iOS 7.0. Для того, щоб status bar в нашому додаток був прозорий, нам потрібно зробити 3 речі:
  1. Змінити info.plist, а саме, змінити ключ UIViewControllerBasedStatusBarAppearance:
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>

  1. Для відображення QQuickView або QQuickWindow використовувати метод showFullScreen() замість show().
  1. Виставити 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;
}

Підсумок на екрані:
ios screenshot
Висновок
Я постарався висвітлити якомога докладніше кожен крок, щоб на основі прикладів із статті ви могли легко доповнити свій мобільний проект нативним кодом. Вихідний код прикладу дивіться на GitHub.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.