Qt on Android:建立可伸縮介面,qtandroid
使用 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)來計算,下面是範例程式碼:
[cpp] view plaincopy
- 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 如下:
[cpp] view plaincopy
- #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 如下:
[cpp] view plaincopy
- #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 ,針對文字、圖片、背景三種情況,做了處理。代碼如下:
[cpp] view plaincopy
- #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()
- {
-
- }
這就是代碼的全部了,雖然簡單,基本可以說明問題了。
部落格之星評選,點擊投我一票,謝謝。投過了也可以點哦,每天都可以投投一票。
完整的項目代碼可以在這裡下載:點擊下載。
其他精彩文章文章
在 android dialog中使用Autocompletetext 大型網站架構設計-Solr mysql雜湊索引 android學習筆記(32)網格視圖(GridView )和圖形切換器(ImageSwi... android學習筆記(31)可展開的列表組件(ExpandableListView )