px和vp之间如何相互转换

px和vp之间如何相互转换

HarmonyOS
2024-05-21 21:53:38
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
kraml

众所周知,终端设备厂商非常多,各种尺寸的手机、平板层出不穷。导致了终端生态环境的碎片化现象越来越严重。为了解决分辨率过多的问题,在HarmonyOS的开发文档中定义了px、vp、fp、lpx,方便开发者适配不同分辨率的终端设备。本文重点介绍介绍px和vp之间如何相互转换。

基础概念

  • px:屏幕真实物理像素单位,比如我们通常说的手机分辨率1496*720都是px的单位。
  • vp:visual    pixel,屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素。
  • fp:font    pixel,字体像素,与vp类似适用屏幕密度变化,随系统字体大小设置变化,比如使用者可以根据自己的偏好来设置系统字体大小。
  •  lpx:视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值。如配置designWidth为720时,在实际宽度为1440物理像素的屏幕上,1lpx为2px大小。
  •  DPI:dots    per inch,屏幕像素密度,每英寸屏幕上拥有的点数。
  •  PPI:pixels    per inch,屏幕像素密度,每英寸屏幕上拥有的像素数。

先看一下不同的手机使用的PPI(屏幕像素密度)不一致,如果都使用px进行计量,同一个控件在不同的手机上表现出来的差异。

可以看到,假如两款设备屏幕像素密度相同,想显示更多的物理像素个数,就需要更大的尺寸;但是手机的尺寸不可能无限增大,在尺寸变化不大的情况下要想显示更多的物理像素个数,就需要更大的屏幕像素密度。使用px作为单位,同样一个布局,如果不同的手机每行物理像素个数总数差异巨大,那么每行显示组件个数差异就会很大,甚至有可能导致布局错乱,这样对普通用户的使用体验极差。而使用vp就基本不会出现这种情况。

为什么vp可以在一定程度上消除设备的差异呢?首先看一下vp的定义:

vp是一台设备针对应用而言所具有的虚拟尺寸(区别于屏幕硬件本身的像素单位),可以根据屏幕像素密度转换为屏幕物理像素。如果用图形来体现就是下图的样子:

不管屏幕分辨率是多少,屏幕密度是多少,组件在视觉上呈现的效果是一致的。

vp具体计算公式为:

vp=pxdensityPixelsvp=\frac{px}{densityPixels} vp=densityPixelspx​

px就是屏幕真实物理像素值,densityPixels是屏幕密度,是和标准DPI的比例,常见取值有0.75,1.0,1.5,2.0,3.0等,在HarmonyOS中,标准DPI为160,所以上述公式便转换成:

vp=pxDPI/160vp=\frac{px}{DPI/160} vp=DPI/160px​

PPI从定义来看,是每英寸屏幕上拥有的像素数,转换成公式就是:

PPI=width2(px)+height2(px)对角线尺寸(inch)PPI=\frac{\sqrt{width^2(px)+height^2(px)}}{对角线尺寸(inch)} PPI=对角线尺寸(inch)width2(px)+height2(px)​

以华为mate 40 pro为例:最大分辨率宽度1344px,高度2772px,屏幕约6.76英寸,根据公式计算得出:

PPI=13442+277226.76=456PPI=\frac{\sqrt{1344^2+2772^2}}{6.76}=456 PPI=6.7613442+27722​​=456

网上大多认为PPI就是DPI,但实际上并不是。从定义来看,DPI是每英寸屏幕上拥有的点数,但一个点并不一定是一个像素。例如:

1. mate 40 pro:ppi = 456;通过API获取到的dpi = 560

2. nexus5:ppi    = (根号下(1920 * 1920 + 1080 * 1080)) / 4.95 = 445ppi;通过API获取到的dpi = 480

3. nexus5X:ppi    = (根号下(1920 * 1920 + 1080 * 1080)) / 5.2 = 424ppi;通过API获取到的dpi = 420

4. note3:ppi    = (根号下(1920 * 1920 + 1080 * 1080)) / 5.5 = 401ppi;通过API获取到的dpi = 480

更多可以参考:https://uiiiuiii.com/screen/

可以看出PPI和DPI没有什么关系,我们平时开发都是用的DPI,这个值不是计算出来的,而是厂商定义的,就姑且认为PPI是实际密度,DPI是系统密度吧。所以我们在实际应用设计开发中说的屏幕密度一般都是系统密度,也就是DPI。当然厂商们应该会有一套规范吧,但是实际情况是千差万别,碎片化就是这么来的。如果所有厂商规定分辨率与dpi一一对应,而且分辨率规格固定,比如1920 * 1070是不合法的,其实没有那么多适配问题,适配非常简单,但是现实是屏幕千差万别,dpi千差万别。

再例如,mate 40 pro默认最小宽度是384vp,在1344 * 2772分辨率下,DPI = 1344 / 384 * 160=560;假如修改成1152 * 2376分辨率,DPI = 1152 / 384 * 160=480。所以对于同一款设备,只要最小宽度vp始终保持不变,每一个vp拥有的尺寸就不会改变,即使在不同分辨率下,同一布局的视觉效果不会存在差异(人眼视觉分辨图形大小是根据实际的物理长度来的,既不是根据像素,也不是根据密度);同理,在不同设备上使用vp作为像素单位,应用组件在视觉上差异不大也是因为不同设备上每一vp拥有的尺寸差异不大。

从UX设计角度

由于终端设备分辨率过多,UX设计师不可能针对每种分辨率都设计应用的原型图,因此光看屏幕的分辨率对于设计师来说是不具备多少实际意义的,屏幕像素密度(DPI)才是设计师要关心的问题。

HarmonyOS支持的屏幕密度取值范围有:



名称




像素密度范围


sdpi

(0, 120]

mdpi

(120, 160]

ldpi

(160, 240]

xldpi

(240, 320]

xxldpi

(320, 480]

xxxldpi

(480, 640]


既然HarmonyOS支持这么多的屏幕密度,在实际应用UX设计过程中,到底依据哪种屏幕作图呢?没有必要为不同密度的手机都提供一套素材,大部分情况下,一套就够了。现在手机比较高的分辨率是1080×1920,你可以选择这个尺寸作图,但是图片素材将会增大应用安装包的大小。并且尺寸越大的图片占用的内存也就越高。现在业界主流是在PS中使用720×1280的画布中作图。这个尺寸兼顾了美观性、经济性和计算的简单。美观性是指,以这个尺寸做出来的应用,在720×1280中显示完美,在1080×1920中看起来也比较清晰;经济性是指,这个分辨率下导出的图片尺寸适中,内存消耗不会过高,并且图片文件大小适中,安装包也不会过大;计算的简单,就是1dp=2px。

但是对于一些全屏铺满的大图,由于不同分辨率下屏幕比例存在差异,确实需要按适配情况切不同尺寸的高保真原型图。

下表列出常用屏幕密度对应的标准分辨率以及标准分辨率下的最小宽度供参考。



名称




dpi




density




标准分辨率




最小宽度


sdpi

120dpi

0.75

240*320px

320vp

mdpi

160dpi

1

320*480px

320vp

ldpi

240dpi

1.5

480*800px

320vp

xldpi

320dpi

2

720*1280px

360vp

xxldpi

480dpi

3

1080*1920px

360vp


从开发者角度

虽然vp可以在一定程度上消除由于终端设备尺寸碎片化带来的兼容性视觉影响,但是不同设备的vp尺寸差异仍然存在,在实际开发过程会发现320dpi基准的高保真原型图无法等比缩放到真机上。所以从开发者的角度,只能尽量消除设备差异,而无法做到绝对消除设备差异。

在HarmonyOS里,想要查看真机的dpi,可以调用display接口查询:

display.getDefaultDisplay() 
  .then(display => { 
    hilog.fatal(0x0000, 'testTag', `display info:${JSON.stringify(display)}`); 
  })

以华为mate 40 pro为例,查询得到的densityPixels=3.5,densityDPI=560。densityDPI就是我们通常所说的系统屏幕密度,densityPixels就是屏幕密度和标准dpi(160)的比率。如果原型图没有提供vp单位的布局,开发者可以根据densityPixel把px转为vp,HarmonyOS也封装了现成的接口px2vp()和vp2px()供开发者直接调用。

在开发过程中,一般会对比如图片、图标采用和原型图1:1比例处理,对于布局、间距采用自适应处理,下图展示的是在实际开发过程中,为了消除设备差异会采用的技术手段。

在上一节说到,xldpi基准的原型图不能兼容所有分辨率的手机设备,所以HarmonyOS提供了限定词目录的解决方案来方便开发者适配不同尺寸下组件的大小。

1. 开发者在resources目录右键菜单选择“New > Resource Directory”,此时可创建资源目录。选择资源组类型,设置限定词,创建后自动生成目录名称。目录名称格式固定为“限定词.资源组”,例如创建一个限定词为屏幕密度下的xldpi,资源组为元素资源的目录,自动生成的目录名称为“xldpi.element”。

1. 在资源目录的右键菜单选择“New > Element Resource File”,即可创建对应资源组目录的资源文件。例如,在xldpi.element目录下可新建integer类型的资源文件。

按照同样的方式还可以按需定义dpi/mdpi/xxldpi/xxxldpi规模下的组件尺寸。在页面开发过程中使用资源访问的方式引用组件尺寸变量后,系统就会自动根据手机屏幕密度适配到相应的尺寸值上。

Image($r('app.media.icon')) 
  .width($r('app.integer.icon_width')) 
  .height($r('app.integer.icon_height'))

最后再看一下fp(font pixel),fp是专门给字体使用的像素单位,可以跟随系统设置的比率(scale)统一对字体进行缩放,因此通常情况下,还是建议使用fp作为字体的单位,除非一些特殊的情况,不想跟随系统字体变化的,可以使用vp。

分享
微博
QQ
微信
回复
2024-05-22 17:40:34
相关问题
Uint8Arraystring之间相互转换
2578浏览 • 1回复 待解决
如何实现PixelMapbase64的相互转换
1587浏览 • 1回复 待解决
HarmonyOS px2vp在模拟器上无法正确转换
387浏览 • 1回复 待解决
vp、fp、px的区别,有谁知道?
2108浏览 • 1回复 待解决
c++ 有vp2px 的接口么?
1724浏览 • 1回复 待解决
px2vp 在context.getLastWindow回调之前无效
212浏览 • 1回复 待解决
flutter鸿蒙如何相互调用?
13932浏览 • 3回复 待解决
是否有Byte KB MB GB之间转换的接口
1730浏览 • 1回复 待解决
HAPHSP之间如何实现数据共享
1759浏览 • 1回复 待解决
卡片app之间的数据是如何交互的
1714浏览 • 1回复 待解决