標籤:qt for android qt on android android
使用 Qt 來開發 Android 應用,也需要適配不同行動裝置,適配多種多樣的螢幕和解析度。這次我們大概來講一下如何使用 Qt 提供的機制來建立可伸縮的介面。
部落格之星評選,點擊投我一票,謝謝。投過了也可以點哦,每天都可以投投一票。
DPI
必須要解釋一下 DPI 。
DPI , dot per inch ,即每英寸包含的點數。還有一個概念是 PPI ,即每英寸包含的像素數。
這個值越大,像素密度越大,小尺寸的螢幕就可以有大解析度。比如有的 Android 手機, 3.7 吋螢幕就能提供 960x540 的解析度,而有的手機, 5 吋螢幕卻提供 800x480 的解析度。這兩種不同螢幕的尺寸和解析度的手機,5 吋屏看起來會有顆粒感,而 3.7 吋看起來則非常細膩。這就是像素密度帶來的差別。
DPI 對介面的影響是醬紫的:同樣解析度(按像素來說)的圖片,在 DPI 越大的螢幕上,看起來就越小。
三類可伸縮元素
一個 Qt 行動裝置 App,大概有三類可伸縮 UI 元素:
我們分別來看一下。
文字
對於文字來講,我們只需要設定特定的文本顯示和輸入控制項所使用的字型(QFont)的 pointSize 即可。像 QLabel 、 QPushButton 、 QLineEdit 等等都適用這種方式。
QFont 的大小有兩種表達方式: pixelSize 和 pointSize 。 pointSize 會根據應用所在的裝置的 DPI 來調整字型,使得在不同 DPI 的裝置上看起來效果一致。
Qt 裡面可以單獨改變一個 Widget 使用的字型,也可以通過 QApplication 來提供全域的字型,這樣那些沒有專門設定的 Widget ,就會使用全域的字型。
圖片
前面我們說了,同樣解析度的圖片,螢幕 DPI 越大,人眼看過去,就覺得越小。
Qt 可以處理這種情況,我們以 QPixmap 為例來說明。
QPixmap 有兩個方法:
- void setDevicePixelRatio(qreal scaleFactor)
- qreal QPixmap::devicePixelRatio() const
這兩個方法操作一個叫作 device pixel ratio 的屬性,這個屬性指定了裝置相關的像素和裝置無關的像素之間的換算比率。我們可以通過調整它來改變一個圖片在手機螢幕上看起來的效果。
QImage 類同樣有這兩個方法。大家可以查閱 Qt 協助來看 API 的細節。
那如何擷取一個裝置的 devicePixelRation 呢?
QScreen 有個方法可以返回這個值:qreal QScreen::?devicePixelRatio() const
QGuiApplication 、 QWindow 這兩個類也有同名的方法。
我們也可以自己計算,使用 QScreen 的 logicalDotsPerInch() 方法結合一個常見的 DPI (比如 72)來計算,下面是範例程式碼:
float SizeUtil::dpiFactor(){ QScreen *screen = qApp->primaryScreen(); return 72 / screen->logicalDotsPerInch();}
我在後面的樣本中用了上面的方法。
要說明的是,Qt 的內建控制項在使用 QPixmap 和 QImage 時,會結合 devicePixelRation 來決定這個控制項的大小,我們的樣本裡使用 QLabel 來顯示圖片。
背景
背景要麼是某種顏色,要麼是一張圖片。當使用圖片做背景時,面臨展開問題。 Android 使用 9patch 圖片來解決這個問題, Qt 也提供了類似的東西:border-image 。
在基於 Qt Widgets 的應用裡,我們可以通過 qss 來設定 border-image ,進而構造可伸縮的背景。
是 Qt 協助裡的,四條線把一張圖片切成了 9 份,使用時,可以保持四個角不變,其它部分通過展開或平鋪填充來適應介面空間大小。
好啦,基本的背景就這麼多了,我們來看一個簡單的例子。
可伸縮介面樣本
我們先看效果後看代碼。
是 PC 上的運行效果:
是手機上的效果,此時圖片沒有設定 devicePixelRatio 。
沒有設定 devicePixelRatio ,圖片看起來要小很多,對比它和文字,可以明顯看出來比例失調。
是設定了 devicePixelRatio 的效果,看起來一致了。
程式碼分析
建立了一個基於 Qt Widgets 的 應用,名字是 scalabeUI ,建立了兩個檔案 sizeUtil.h 和 sizeUtil.cpp 。
項目裡用到了兩個圖片資源:
圖片我加到了 qrc 裡。
sizeUtil.h 如下:
#ifndef SIZEUTIL_H#define SIZEUTIL_H#include <QFont>#include <QString>class SizeUtil{private: SizeUtil(){} SizeUtil(const SizeUtil &); SizeUtil & operator=(const SizeUtil&);public: ~SizeUtil(){} static SizeUtil & instance(); int defaultFontHeight(); int widthWithDefaultFont(const QString &text); int widthWithFont(const QString &text, int fontPointSize); int fontHeight(int fontPointSize); float dpiFactor();};#endif // SIZEUTIL_H
sizeUtil.cpp 如下:
#include "sizeUtil.h"#include <QApplication>#include <QFontMetrics>#include <QScreen>SizeUtil & SizeUtil::instance(){ static SizeUtil util; return util;}int SizeUtil::defaultFontHeight(){ return qApp->fontMetrics().height();}int SizeUtil::widthWithDefaultFont(const QString &text){ return qApp->fontMetrics().boundingRect(text).width();}int SizeUtil::widthWithFont(const QString &text, int fontPointSize){ QFont f = qApp->font(); f.setPointSize(fontPointSize); QFontMetrics fm(f); return fm.boundingRect(text).width();}int SizeUtil::fontHeight(int fontPointSize){ QFont f = qApp->font(); f.setPointSize(fontPointSize); QFontMetrics fm(f); return fm.height();}float SizeUtil::dpiFactor(){ QScreen *screen = qApp->primaryScreen(); return 72 / screen->logicalDotsPerInch();}
SizeUtil 類主要是用來計算文本的像素大小。
建立項目嚮導給我們產生了 widget.cpp 和 widget.h ,我修改了一下 widget.cpp ,針對文字、圖片、背景三種情況,做了處理。代碼如下:
#include "widget.h"#include <QVBoxLayout>#include <QHBoxLayout>#include <QLabel>#include <QPushButton>#include "sizeUtil.h"Widget::Widget(QWidget *parent) : QWidget(parent){ QVBoxLayout *layout = new QVBoxLayout(this); //case 1. background QLabel *label = new QLabel("Hello Scalable Label"); layout->addWidget(label, 1); /* top right bottom left */ label->setStyleSheet( "QLabel{border-image:url(:/bkgnd.9.png) 38 6 6 16;" " border-left-width: 16; border-top-width: 38;" " border-right-width: 6; border-bottom-width: 6}"); //case 2. image QLabel *head = new QLabel; QPixmap orig(":/head.png"); orig.setDevicePixelRatio(SizeUtil::instance().dpiFactor()); head->setPixmap(orig); layout->addWidget(head); //case 3. text button QHBoxLayout *hlayout = new QHBoxLayout; layout->addLayout(hlayout); QPushButton *button = new QPushButton("Text Button"); hlayout->addWidget(button); hlayout->addStretch(1);}Widget::~Widget(){}
這就是代碼的全部了,雖然簡單,基本可以說明問題了。
部落格之星評選,點擊投我一票,謝謝。投過了也可以點哦,每天都可以投投一票。
完整的項目代碼可以在這裡下載:點擊下載。
------------回顧一下 Qt on Android 系列:
Qt on Android:圖文詳解Hello World全過程
Windows下Qt 5.2 for Android開發入門
Qt for Android 部署流程分析
Qt on Android:將Qt調試資訊輸出到logcat中
Qt on Android: Qt 5.3.0 發布,針對 Android 改進說明
Qt on Android Episode 1(翻譯)
Qt on Android Episode 2(翻譯)
Qt on Android Episode 3(翻譯)
Qt on Android Episode 4(翻譯)
Qt for Android 編譯純C工程
Windows下Qt for Android 編譯安卓C語言可執行程式
Qt on Android: Android SDK安裝
Qt on Android: http下載與Json解析
Qt on Android 之設定應用程式名稱為中文
Qt on Android:讓 Qt Widgets 和 Qt Quick 應用全螢幕顯示
Qt on Android:怎樣適應不同的螢幕尺寸
Qt on Android:使用JNI與第三方jar包
《Qt on Android核心編程》介紹
Qt on Android:資源檔系統qrc與assets
Qt on Android:建立可伸縮介面