首页 > 技术文章 > 2.1【HarmonyOS鸿蒙开发】单位vp、fp、px

hanru 2021-06-30 09:20 原文

作者:韩茹

公司:程序咖(北京)科技有限公司

鸿蒙巴士专栏作家

当年的Android中,有的sp、dp。还有dpi等概念。

  • dpi,dots per inch,代表屏幕像素密度。
  • dp,device independent pixels(设备独立像素,等同于dip),不依赖于像素
  • sp,scale-independent pixels(缩放独立像素,等同于sip),和dp类似,允许由用户自定义文字尺寸大小(如小、正常、大、超大等)

所以在Android应用程序的mipmap或drawable目录下,根据dpi的不同分为ldpi、mdpi、hdpi、xhdpi、xxhdpi、xxxhdpi等。

HarmonyOS在借鉴Android经验的同时,也提出了创新。

对于很大一部分程序员用户来说,会比较纠结这些问题:图片的大小尺寸定义为多大尺寸比较合理?应该放在什么标准的dpi中才能得到更好的适配效果?在HarmonyOS中,程序员可不必将时间和精力过多花费在此。在放置图片的media目录下也没有按照dpi来进行区分。

为了解决由于屏幕规格不同而引起的页面适配问题,HarmonyOS 提供了针对不同屏幕尺寸进行界面自适应适配的7种原子布局能力,使设计师可以使用原子布局能力来定义元素在不同尺寸的界面上的自适应规则。详见官方文档:https://developer.harmonyos.com/cn/docs/design/des-guides/layout-0000001111593946

HarmonyOS 重新定义了界面换算单位,使用虚拟像素作为一台设备针对应用而言所具有的虚拟尺寸,是定义应用内参数尺寸的度量单位。虚拟像素也是一种可灵活使用和缩放的单位,它与屏幕像素的关系是 1vp 约等于 160dpi 屏幕密度设备上的 1px。在不同密度的设备之间,HarmonyOS 会针对性的转换设备间对应的实际像素值。

一、px

px:像素的单位,1px代表手机屏幕上的一个像素点。比如常见的手机分辨率有320×480,480×800,1080×1920等,这些数值的单位都是px;由于px在不同手机上的大小不同,差别较大,适配性太差,不建议使用。
由于手机分辨率不同,应用中用到的图片某个固定尺寸大小的图片,或某个固定字号的文本就会因手机分辨力不同而出现失真或变形的情况,为了更好的适配不同的手机,故而就有了不依赖与像素的单位sp与dp。

二、vp

vp:虚拟像素(virtual pixel)是一台设备针对应用而言所具有的虚拟尺寸(区别于屏幕硬件本身的像素单位)。它提供了一种灵活的方式来适应不同屏幕密度的显示效果。

0000000000011111111.20210603201428.92689073733780637554576696276784

使用虚拟像素vp,使元素在不同密度的设备上具有一致的视觉体量。

三、fp

fp,font-size pixels,字体像素单位,其大小规范默认情况下与vp相同,但如果开发者在设置中修改了字体显示大小,就会在vp的基础上乘以scale系数。即默认情况下 1 fp = 1vp,如果设置了字体显示大小,则会根据实际情况自动设置 1fp = 1vp * scale。

四、AttrHelper工具类

为了方便开发者对尺寸长度的管理,官方提供了AttrHelper工具类,可实现fp、vp、px之间的相互转换,简直不要太方便。以下是部分方法:

Modifier and Type Method Description
static int fp2px(float value, float density) Converts a font-size pixel (fp) to a pixel value based on the screen density.
static int fp2px(float value, float density, float fontRatio) Converts a font-size pixel (fp) value to a pixel value based on the screen density and font ratio.
static int fp2px(float value, Context context) Converts a font-size pixel (fp) to a pixel value based on the screen context.
static float getDensity(Context context) Obtains the display density.
static int vp2px(float value, float density) Converts a virtual pixel (vp) to a pixel value based on the screen density.
static int vp2px(float value, Context context) Converts a virtual pixel (vp) to a pixel value based on the screen context.

另外,除fp、vp、px的相互转换API以外,该工具类还提供了关于density、字体名称与其值等的转换方法,非常非常的贴心。详见官方文档:https://developer.harmonyos.com/cn/docs/documentation/doc-references/attrhelper-0000001054518726

在HarmonyOS中,我们想布局一个页面,可以使用xml文件,也可以通过代码的形式。这里我们使用之前讲PositionLayout布局的一个例子。

重新创建一个布局文件:position_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<PositionLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:background_element="#99FDF5E6"
    >

    <Text
        ohos:id="$+id:text1"
        ohos:height="50vp"
        ohos:width="200vp"
        ohos:background_element="#99FFE4B5"
        ohos:layout_alignment="horizontal_center"
        ohos:text="Title"
        ohos:text_size="20fp"
        ohos:text_alignment="center"
        />
    <Text
        ohos:id="$+id:text2"
        ohos:height="200vp"
        ohos:width="200vp"
        ohos:background_element="#99FFE4B5"
        ohos:text="Content"
        ohos:text_alignment="center"
        ohos:text_size="20fp"/>
    <Text

        ohos:id="$+id:text3"
        ohos:height="200vp"
        ohos:width="200vp"
        ohos:background_element="#99FFE4B5"
        ohos:text="Content"
        ohos:text_alignment="center"
        ohos:text_size="20fp"/>
</PositionLayout>

然后打开slice下的MainAbilitySlice文件,首先修改一下要加载的布局文件:

super.setUIContent(ResourceTable.Layout_position_layout);

然后获取Text组件,并设置positon,其实就是通过id获取Text组件,想要设置X轴和Y轴的偏移量,调用setPosition()方法,来进行设置即可。

public void setPosition(int coordX, int coordY);

//Sets the position of the upper-left corner of a component, relative to the parent layout.

但是该方法的参数为int类型,单位为px。如果想使用vp或者fp等单位。需要借助于一个类AttrHelper。

其实在代码中,很多方法的参数设置都是以px为单位。

Java示例代码如下:

package com.example.positionlayout.slice;

import com.example.positionlayout.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.Text;

public class MainAbilitySlice extends AbilitySlice {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_position_layout);

        //获取Text组件
        Text title = (Text)findComponentById(ResourceTable.Id_text1);
        Text content1 = (Text)findComponentById(ResourceTable.Id_text2);
        Text content2 = (Text)findComponentById(ResourceTable.Id_text3);
//        ohos:position_x="200"
//        ohos:position_y="60"
        //这里的参数200,60,单位都是像素px。
        title.setPosition(200, 60);
        /*
        因为在xml布局文件中,我们使用的是vp,不是px,但是setPosition()方法的单位是px,所以这里我们转换一下。需要使用AttrHelper类。

        ohos.agp.components.AttrHelper类:
        提供了vp,fp到px的转换
        vp2px(float value, float density)-->static int基于屏幕密度将虚拟像素(vp)转换为像素值。
         */
        //AttrHelper.vp2px(20, AttrHelper.getDensity(this));//将20vp转为px

//        ohos:position_x="20vp"
//        ohos:position_y="100vp"
        content1.
          (AttrHelper.vp2px(20, AttrHelper.getDensity(this)), AttrHelper.vp2px(100, AttrHelper.getDensity(this)));
//        ohos:position_x="150vp"
//        ohos:position_y="250vp"
        content2.setPosition(AttrHelper.vp2px(150, AttrHelper.getDensity(this)), AttrHelper.vp2px(250, AttrHelper.getDensity(this)));
    }

}

然后启动模拟器运行:

WX20210607-113258@2x

这里需要格外强调一下,在设置控件的宽度和高度上,我们习惯了在xml布局中使用vp而不使用px(像素)。在设置字体大小的时候,我们使用fp也不使用px。

特别鸣谢:

https://www.harmonybus.net/archives/2048

更多内容:

1、社区:鸿蒙巴士https://www.harmonybus.net/

2、公众号:HarmonyBus

3、技术交流QQ群:714518656

4、视频课:https://www.chengxuka.com

推荐阅读