在构建鸿蒙应用程序中,经常需要定义组件或字体的尺寸,而这些组件和字体最终会呈现在屏幕上,因此设计时需要考虑屏幕的尺寸和分辨率。开发者和设计师需要掌握一些关于像素的基本概念。
无论设备屏幕显示技术采用的是LCD(Liquid Crystal Display)还是OLED(Organic Light-Emitting Diode),五彩缤纷的画面都是通过能够表达颜色的发光点阵实现的,这其中的每个点都是屏幕中能够显示的最小且不可分割的单元,称为物理像素,简称像素(pixel,px)。
在之前,消费者经常通过分辨率评判一个屏幕的优劣,而分辨率就是指屏幕在横向和纵向上像素的数量。例如,华为P40手机的屏幕分辨率为2340×1080。这说明,华为P40的屏幕在纵向上最多排列了2340像素,在横向上最多排列了1080像素,而像素总数约为2340和1080的乘积,但是,目前绝大多数的手机和平板计算机采用了异形屏幕设计(例如,屏幕的4个角具有弧度、挖孔屏、刘海屏、水滴屏等),实际的像素要略少于分辨率的数值乘积。
长期以来,设计师和开发者经常以像素为单位设计组件的尺寸。例如,定义某个文本组件宽度为300px,高度为50px,那么可以直接在代码中指定其像素数值,代码如下:
如果不带单位,则默认的单位也是像素数值,代码如下:
这段代码的含义与上段代码相同。这个组件在屏幕中在横向上会占据300像素宽度,在纵向上会占据48像素高度,最终会以300×48像素展现这个组件。
在过去的很长一段时间内,用户只要细心观察,就能够从屏幕上分辨出像素阵的存在,即存在“颗粒感”。事实上,导致屏幕颗粒感的主要因素并不是分辨率,而是像素密度。像素密度(Pixels Per Inch,PPI)是指屏幕上每英寸距离上的像素数量。
随着技术的进步,屏幕的像素密度正在不断升高,肉眼看上去也就越来越清晰。2010年苹果公司推出了视网膜屏幕(Retina Display),PPI达到了326。这样的屏幕在一定的视距上肉眼就完全感知不到“颗粒感”了。事实上,PPI在300以上,人眼在使用移动设备时就几乎无法感知像素的存在了。
各种设备的PPI差别很大。手机屏幕的PPI非常敏感,绝大多数的手机PPI大于300,例如华为P40手机的PPI达到了480。有些手机的PPI甚至超过了500。对于可穿戴设备来讲,受限于续航能力其PPI往往较低。对于车机和智慧屏来讲,由于其观看的视距较远,用户对于PPI的敏感度较低,因此通常其也就在300左右。
注意: 还存在与PPI类似的概念:DPI。DPI(Dots Per Inch)通常是针对打印机等输出设备而言的,是指每英寸距离上的墨点数量,但是,有些开发者也将DPI的概念用在屏幕上,此时PPI与DPI所表达的意义是相同的。
设备的PPI不同会导致一个问题:同样像素大小的组件显示在不同PPI的屏幕上其大小也不同。如果两块屏幕的PPI相差一倍,则显示在这两块屏幕上的组件大小也会缩放一倍,如图3-9所示。
图3-9 通过像素(单位:px)定义组件大小会在不同PPI屏幕上呈现出不同的效果
这显然不是开发者和用户所希望的。为了解决这个问题,这里引出了一个新的概念:虚拟像素。
虚拟像素(virtual pixel,vp)是指与设备屏幕PPI无关的抽象像素。通过虚拟像素定义的组件,在不同PPI屏幕上显示时其实际的显示大小是相同的。那么这是怎样实现的呢?首先,定义在160PPI屏幕密度设备上,一个虚拟像素约等于一个物理像素。由此,在其他PPI设备上通过以下公式将虚拟像素的大小实时转换为物理像素的大小即可:
物理像素(px)=虚拟像素(vp)×屏幕密度(PPI)/160
例如,30vp在华为P40(PPI为480)上的物理像素大小约为30×480/160=90(px),而在PPI为320的设备上的物理像素约为30×320/160=60(px)。由此可见,以vp为单位定义的长度在高PPI设备上的实际物理像素大小要高于低PPI设备上的实际物理像素大小,并且成正比例关系。最终,以vp为单位的长度与屏幕密度无关,在不同屏幕上所显示出的物理长度是相同的。
再如,定义某个文本组件宽度为100vp,高度为16vp,代码如下:
这个文本组件在160PPI和320PPI的屏幕上的显示效果趋于一致,唯一不同的是后者屏幕上显示的文字会更加清晰,如图3-10所示。
图3-10 通过虚拟像素(单位:vp)定义组件大小会在不同PPI屏幕上呈现出类似的效果
字体像素(font-size pixel,fp)的概念与虚拟像素类似,定义如下:
物理像素(px)=字体像素(fp)×屏幕密度(PPI)/160
但是,字体像素通常应用在文本的字号上。例如,可以通过text_size属性定义文本的字号为16fp,代码如下:
这个组件中的文本内容在屏幕的纵向上占据了16个字体像素长度。通过上面的公式可以换算出在480PPI的设备(例如华为P40手机)上,其实际的物理像素长度约为16×480/160=48(px),因此,以下代码的显示效果与上面的代码相同:
这个组件中的文本内容在屏幕的纵向上占据了48个像素长度。
使用字体像素还有一个优势,应用程序中的字体大小可以跟随系统显示大小。用户可以在鸿蒙操作系统的【设置】→【显示与亮度】→【字体与显示大小】→【显示大小】选项中改变应用程序中由字体像素定义的字体大小。
在Java代码中,通过布局配置(LayoutConfig)对象设置组件的宽度和高度时,所传入的数值为物理像素。如果开发者希望设置虚拟像素,那么就涉及像素转换问题了。
为了可以使用虚拟像素(字体像素)与物理像素之间的计算公式,首先需要通过Java代码获得当前设备的屏幕密度(PPI),代码如下:
然后,就可以实现将虚拟像素和字体像素转换为物理像素了,代码如下:
随后,就可以应用toPixels方法为组件设置以虚拟像素(vp)为单位的高度和宽度,以及以字体像素(fp)为单位的字号了,代码如下:
有时,为了能够更加精准地布局组件,需要获取设备屏幕的宽度和高度,代码如下:
需要注意的是,通过这种方法获取的设备屏幕的宽度和高度单位为虚拟像素单位。