使用 Qt 来开发 Android 应用,也需要适配不同移动设备,适配多种多样的屏幕和分辨率。这次我们大概来讲一下如何使用 Qt 提供的机制来创建可伸缩的界面。

DPI

必须要解释一下 DPI 。

DPI , dot per inch ,即每英寸包含的点数。还有一个概念是 PPI ,即每英寸包含的像素数。

这个值越大,像素密度越大,小尺寸的屏幕就可以有大分辨率。比如有的 Android 手机, 3.7 吋屏幕就能提供 960x540 的分辨率,而有的手机, 5 吋屏幕却提供 800x480 的分辨率。这两种不同屏幕的尺寸和分辨率的手机,5 吋屏看起来会有颗粒感,而 3.7 吋看起来则非常细腻。这就是像素密度带来的差别。

DPI 对界面的影响是酱紫的:同样分辨率(按像素来说)的图片,在 DPI 越大的屏幕上,看起来就越小。

三类可伸缩元素

一个 Qt 移动应用,大概有三类可伸缩 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)来计算,下面是示例代码:

  1. float SizeUtil::dpiFactor()
  2. {
  3. QScreen *screen = qApp->primaryScreen();
  4. return 72 / screen->logicalDotsPerInch();
  5. }

我在后面的示例中用了上面的方法。

要说明的是,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 如下:

  1. #ifndef SIZEUTIL_H
  2. #define SIZEUTIL_H
  3. #include <QFont>
  4. #include <QString>
  5. class SizeUtil
  6. {
  7. private:
  8. SizeUtil(){}
  9. SizeUtil(const SizeUtil &);
  10. SizeUtil & operator=(const SizeUtil&);
  11. public:
  12. ~SizeUtil(){}
  13. static SizeUtil & instance();
  14. int defaultFontHeight();
  15. int widthWithDefaultFont(const QString &text);
  16. int widthWithFont(const QString &text, int fontPointSize);
  17. int fontHeight(int fontPointSize);
  18. float dpiFactor();
  19. };
  20. #endif // SIZEUTIL_H

sizeUtil.cpp 如下:

  1. #include "sizeUtil.h"
  2. #include <QApplication>
  3. #include <QFontMetrics>
  4. #include <QScreen>
  5. SizeUtil & SizeUtil::instance()
  6. {
  7. static SizeUtil util;
  8. return util;
  9. }
  10. int SizeUtil::defaultFontHeight()
  11. {
  12. return qApp->fontMetrics().height();
  13. }
  14. int SizeUtil::widthWithDefaultFont(const QString &text)
  15. {
  16. return qApp->fontMetrics().boundingRect(text).width();
  17. }
  18. int SizeUtil::widthWithFont(const QString &text, int fontPointSize)
  19. {
  20. QFont f = qApp->font();
  21. f.setPointSize(fontPointSize);
  22. QFontMetrics fm(f);
  23. return fm.boundingRect(text).width();
  24. }
  25. int SizeUtil::fontHeight(int fontPointSize)
  26. {
  27. QFont f = qApp->font();
  28. f.setPointSize(fontPointSize);
  29. QFontMetrics fm(f);
  30. return fm.height();
  31. }
  32. float SizeUtil::dpiFactor()
  33. {
  34. QScreen *screen = qApp->primaryScreen();
  35. return 72 / screen->logicalDotsPerInch();
  36. }

SizeUtil 类主要是用来计算文本的像素大小。

新建项目向导给我们生成了 widget.cpp 和 widget.h ,我修改了一下 widget.cpp ,针对文字、图片、背景三种情况,做了处理。代码如下:

  1. #include "widget.h"
  2. #include <QVBoxLayout>
  3. #include <QHBoxLayout>
  4. #include <QLabel>
  5. #include <QPushButton>
  6. #include "sizeUtil.h"
  7. Widget::Widget(QWidget *parent)
  8. : QWidget(parent)
  9. {
  10. QVBoxLayout *layout = new QVBoxLayout(this);
  11. //case 1. background
  12. QLabel *label = new QLabel("Hello Scalable Label");
  13. layout->addWidget(label, 1);
  14. /* top right bottom left */
  15. label->setStyleSheet(
  16. "QLabel{border-image:url(:/bkgnd.9.png) 38 6 6 16;"
  17. " border-left-width: 16; border-top-width: 38;"
  18. " border-right-width: 6; border-bottom-width: 6}");
  19. //case 2. image
  20. QLabel *head = new QLabel;
  21. QPixmap orig(":/head.png");
  22. orig.setDevicePixelRatio(SizeUtil::instance().dpiFactor());
  23. head->setPixmap(orig);
  24. layout->addWidget(head);
  25. //case 3. text button
  26. QHBoxLayout *hlayout = new QHBoxLayout;
  27. layout->addLayout(hlayout);
  28. QPushButton *button = new QPushButton("Text Button");
  29. hlayout->addWidget(button);
  30. hlayout->addStretch(1);
  31. }
  32. Widget::~Widget()
  33. {
  34. }

这就是代码的全部了,虽然简单,基本可以说明问题了。

博客之星评选,点击投我一票,谢谢。投过了也可以点哦,每天都可以投投一票。

完整的项目代码可以在这里下载:点击下载

其他精彩文章文章

在 android dialog中使用Autocompletetext
大型网站架构设计-Solrmysql哈希索引
android学习笔记(32)网格视图(GridView )和图形切换器(ImageSwi...
android学习笔记(31)可展开的列表组件(ExpandableListView )

Qt on Android:创建可伸缩界面的更多相关文章

  1. 创建一个QT for Android的传感器应用应用程序(摘自笔者2015年将出的《QT5权威指南》,本文为试读篇)

     这个手册描述了使用Qt Quick面访的方式在Android和ios设备上开发QtQuick应用程序的方法.我们使用Qt Creator实现一个QtQuick应用程序,这个应用程序基于加速器的值 ...

  2. Android研究之动态创建UI界面具体解释

     Android的基本UI界面一般都是在xml文件里定义好,然后通过activity的setContentView来显示在界面上.这是Android UI的最简单的构建方式.事实上,为了实现更加复 ...

  3. Qt on Android 核心编程

    Qt on Android 核心编程(最好看的Qt编程书!CSDN博主foruok倾力奉献!) 安晓辉 著   ISBN 978-7-121-24457-5 2015年1月出版 定价:65.00元 4 ...

  4. Qt on Android 蓝牙开发

    版权声明:本文为MULTIBEANS ORG研发跟随文章,未经MLT ORG允许不得转载. 最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本 ...

  5. Qt on Android: Qt Quick 之 Hello World 图文具体解释

    在上一篇文章,<Qt on Android:QML 语言基础>中,我们介绍了 QML 语言的语法,在最后我们遗留了一些问题没有展开,这篇呢,我们就正式開始撰写 Qt Quick 程序,而那 ...

  6. Qt on Android: Qt 5.3.0 公布,针对 Android 改进的说明

    5月20日本,Qt 官方博客宣布 Qt 5.3.0 公布! 这个版本号聚焦在性能.稳定性和可用性的提升上,与 5.1 / 5.2 相比有非常大提升. 5.3.0 的主要变化: 稳定能.可用性大大提升 ...

  7. Qt on Android 蓝牙通信开发

    版权声明:本文为MULTIBEANS ORG研发跟随文章,未经MLT ORG允许不得转载. 最近做项目,需要开发安卓应用,实现串口的收发,目测CH340G在安卓手机上非常麻烦,而且驱动都是Java版本 ...

  8. Qt on Android: http下载与Json解析

    百度提供有查询 ip 归属地的开放接口,当你在搜索框中输入一个 ip 地址进行搜索,就会打开由 ip138 提供的百度框应用,你能够在框内直接输入 ip 地址查询.我查看了页面请求,提取出查询 ip ...

  9. Qt for Android修改应用程序的图标和名称

    使用QT开发出的Android Apk安装后默认的图标是安卓的小机器人,下面介绍在QT5.12版本上修改APP名称和图标的方法. 1.  编译一次项目后,在编译目录下找到AndroidManifest ...

随机推荐

  1. 【oracle】union、union all、intersect、minus 的用法及区别

    一.union与union all 首先建两个view create or replace view test_view_1 as as c from dual union as c from dua ...

  2. linux I/O stack cache 强制刷新

    linux 存储子系统作为最为复杂的子系统之一,拥有很深的模块栈(如图),其中很多模块又有自己的缓存功能(如下图).实际应用中,用户下发的数据停留在哪个缓存中,是否已经写入磁盘,这些操作对用户来说是个 ...

  3. mac搭建nginx与php

    第一步:关闭Apache及开机启动 要使用nginx,最好停用mac中自带的Apache.停用很简单: sudo launchctl unload -w /System/Library/LaunchD ...

  4. 【BFS】POJ 3414

    直达 -> POJ 3414 Pots 相似题联动–>HDU 1495 非常可乐 题意:两个壶倒水,三种操作,两个桶其中一个满足等于C的最少操作,输出路径.注意a,b互倒的时候能不能倒满, ...

  5. 我听说 C...

    我听说在 c 语言的世界里,goto 和异常处理都是声名狼藉的东西,而我认为它们在一起就能化解各自的问题.

  6. 案例1.通过Jquery来处理复选框

    实现以下功能: 1:选中第一个复选框,那么下面所有的复选框都选中,去除选中第一个复选框,下面的都不选中 2:当点击全选按钮,上面足球.篮球.游泳.唱歌 全部选中 3:当点击全不选按钮,上面四个全部取消 ...

  7. ceil 模块

    # 有时需要得到一个最小的整数,而这个数只能比自己大或相等,不能小于自己 #如: 2.1 我们需要得到的最小整数为3,即使后一位只有很小的一部分,一般用于分页 from math import cei ...

  8. bzoj 刷水

    bzoj 3856: Monster 虽然是sb题,,但是要注意h可能<=a,,,开始忘记判了WA得很开心. #include <iostream> #include <cst ...

  9. 新版startssl 免费SSL证书申请 (实测 笔记 https http2 必要条件)

    简单说明: 目前多个大型网站都实现全站HTTPS,而SSL证书是实现HTTPS的必要条件之一. StartSSL是StartCom公司旗下的.提供免费SSL证书服务并且被主流浏览器支持的免费SSL.包 ...

  10. CentOS7 编译安装 Mongodb (实测 笔记 Centos 7.0 + Mongodb 2.6.6)

    环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7.0-1406-x86_64-DVD.iso 安装步骤: 1.准备 1.1 显示系统版 ...