だらだらアプリを書いている間に思いついた方法を書いてみます.
QMLの不便だなと感じるところ
QMLにはAndroidのdpみたいなデバイス間でいい感じにUIのサイズを調整するための単位がありません.デスクトップ・モバイル両対応のアプリを作りたいとき,様々なモバイルデバイスに対応したアプリを作りたいときなどなどに,各々の開発者がなんらか工夫しなければなりません
解決策
1 画面サイズに対する比でUIのサイズを決める
ヘッダーバーの高さは画面の高さの1/10とか,ボタンの幅は画面の幅の2/3とか.しかし,これだと画面を回転させるとUIのサイズが変わることになってしまいます.
2 物理的な単位を用いる
main.qmlでimport QtQuick.Window 2.1
とし,
property real mm: Screen.pixelDensity
と書いておくと,main.qmlから利用される全てのQMLファイル内でmm単位を利用できるので,width: 5*mm
のようにして長さを指定できます.
しかし全てのデバイスでUIの物理的な大きさが同じというのはやはり違います.物理サイズの大きな画面ではUI部品を大きく,小さな画面では小さく表示したいものです
3 Androidのdpのような単位を作る
この方法は.qmlprojectを利用したアプリには利用できません.
先ず,プロジェクトファイル(.pro)に次の一文を追加します(Android対応しない場合は不要です).
android : QT += androidextras
次に,main.cppに#include
文を追加し,main関数に次のような内容を追記します.
#include <QScreen>
#ifdef Q_OS_ANDROID
#include <QtAndroidExtras/QAndroidJniObject>
#endif
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QQmlApplicationEngine engine();
//implement density-independent pixel
int dp = 1; // 普通の解像度のPCでは1dp = 1pixel
// PC向けコード 1inchあたりのpixel数を125で割る
qreal dotsPerInch = app.screens()[0]->physicalDotsPerInch();
qDebug() << "physicalDotsPerInch = " << dotsPerInch;
dp = qRound(dotsPerInch) / 125;
#ifdef Q_OS_ANDROID
// Androidにて1dpが何pixelであるか取得するためのコード
qDebug() << "Android OS detected";
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
QAndroidJniObject resource = activity.callObjectMethod("getResources","()Landroid/content/res/Resources;");
QAndroidJniObject metrics = resource.callObjectMethod("getDisplayMetrics","()Landroid/util/DisplayMetrics;");
dp = metrics.getField<float>("density");
#endif
#ifdef Q_OS_IOS
// iOS用コード
qDebug() << "iOS detected";
dp = 3;
#endif
qDebug() << "1dp = " << dp;
// 定数dpを登録
engine.rootContext()->setContextProperty(QLatin1String("dp"), QVariant::fromValue(dp));
engine.load(QUrl("main.qml"));
QObject *topLevel = engine.rootObjects().value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
if ( !window ) {
qWarning("Error: Your root item has to be a Window.");
return -1;
}
window->show();
return app.exec();
}
QQmlApplicationEngine
ではなくQQuickView
を使っている場合は,engine.rootContext()
をview.rootContext()
としてください.
QtQuick2ApplicationViewer
を使っている場合も,viewer.rootContext()
としてください.
注意するポイントとしては,engine.rootContext()->setContextProperty(QLatin1String("dp"), QVariant::fromValue(dp));
の後にengine.load(QUrl("main.qml"));
またはview.setSource(QUrl("main.qml"));
,またはviewer.setMainQmlFile(main.qml");
としてQMLファイルをロードしてください.
こうすることで,dpという定数が認識され,QMLファイル内でwidth: 5*dp
のように使用できます.
動作確認
Android(GN)と普通の解像度のディスプレイ(13inch,1366x768)でのみ確認しているため,高解像度ディスプレイとiOSでは確認しておりません.不具合があればコメントにてご連絡ください.
また、もっといい方法があるよ!という方はぜひ記事にして教えて下さい。
参考
[RFC] Using DP instead of PX for Android http://qt-project.org/forums/viewthread/35287
implement dp unit(筆者のアプリ) https://github.com/yuntan/Aztter/commit/382e9b6508e8885b5dc817b5dd4708cf85035acc#diff-118fcbaaba162ba17933c7893247df3aR19