在日常应用页面布局设计时,开发者需要知道每个组件的样式及位置,这时就需要了解像素单位及相互转换方法,ArkUI 开发框架提供了4种像素单位供开发者使用,分别是: px 、 vp 、 fp 和 lpx ,框架采用 vp 为基准数据单位,本篇就简单为大家介绍下像素单位的基本知识与像素单位转换API的使用。通过像素转换案例,向开发者讲解了如何使用像素单位设置组件的尺寸、字体的大小以及不同像素单位之间的转换方法。
效果呈现
本例最终效果如下:
运行环境
本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发:
- IDE: DevEco Studio 3.1 Release
- SDK: Ohos_sdk_public 3.2.12.5(APIVersion 9 Release)
实现思路
本篇案例主要功能包括:①像素单位基本知识介绍;②像素单位转换相关API的使用。
- 构建入口页面:该页面包含两个button组件,通过点击按钮事件,实现到详细页面的跳转
- 像素单位介绍页面:
- 构建IntroducitonViewModel.ets
创建自定义接口IntroductionItem,根据IntroductionItem接口参数,创建对象数组INTRODUCE_LIST,向对象数组INTRODUCE_LIST中填充像素单位介绍页面所需参数内容。
- 构建IntroducitonPage.ets
通过ForEach循环渲染上一步骤中对象数组中的每个Item;通过if判断组件的显隐,同时添加样式,完成像素介绍页面。
- 像素转换页面:
- 构建ConvertionViewModel.ets
创建自定义接口ConversionItem,根据ConversionItem接口参数,创建对象数组ConversionViewModel,向对象数组ConversionViewModel中填充像素转换页面所需参数内容。
- 构建ConvertionPage.ets
通过ForEach循环渲染上一步构建的ConversionViewModel的每个子菜单Item,同时添加样式,构建像素介绍页面。
开发步骤
1.构建入口页面:该页面包含两个button按钮,通过点击按钮事件,实现到详细页的跳转。 具体代码如下:
// entry/src/main/ets/pages/IndexPage.ets
import router from '@ohos.router';
@Entry
@Component
struct IndexPage {
// 定义jumpPage方法,实现路由跳转
jumpPage(url: string) {
router.pushUrl({ url })
}
build() {
Column({ space: 24 }) {
Button('像素介绍')
.height('40vp')
.width('100%')
.backgroundColor($r('app.color.blue_background'))
// 点击时回调jumpPage方法,跳转到pages/IntroductionPage页面
.onClick(() => this.jumpPage('pages/IntroductionPage'))
Button('像素转换')
.height('40vp')
.width('100%')
.backgroundColor($r('app.color.blue_background'))
// 点击时回调jumpPage方法,跳转到pages/ConversionPage页面
.onClick(() => this.jumpPage('pages/ConversionPage'))
}
.backgroundColor($r('app.color.page_background'))
.justifyContent(FlexAlign.Center)
.padding(24)
.width('100%')
.height('100%')
}
}
2.像素单位介绍页面创建。 此页面主要系统介绍像素单位的概念,包含px、vp、lpx以及fp,并在页面中 为Text组件的宽度属性设置不同的像素单位(如px、vp、lpx),fp像素单位则设置为Text组件的字体大小。
- 从效果图看,此页面由4个功能相同的菜单组成,我们先构建功能菜单。创建IntroducitonViewModel.ets定义每个子功能菜单Item。 具体代码如下:
// entry/src/main/ets/viewmodel/IntroducitonViewModel.ets
// 创建自定义接口,定义每个Item中的内容
interface IntroductionItem {
name: string;
title: string;
subTitle: string;
value: string;
smallFontSize: number;
largeFontSize: number;
}
// 根据自定义接口IntroductionItem,填充内容
const INTRODUCE_LIST: IntroductionItem[] = [
{
name: 'px',
title: '屏幕物理像素单位。',
subTitle: null,
value: '200px',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'vp',
title:'屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素。',
value:'200vp',
subTitle:'像素密度为160dpi的设备上1vp=1px,1vp对应的物理屏幕像素=(屏幕像素密度/160)px',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'lpx',
title:'视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值。',
subTitle: null,
value: '200lpx',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'fp',
title:'字体像素,与vp类似,随系统字体大小设置变化。',
subTitle:'默认情况下与vp相同,即1vp=1fp,如果用户手动调整了系统字体,scale为缩放比例,设置后的字体大小(单位fp) = 设置前的字体大小 * scale',
value: '',
smallFontSize: 14,
largeFontSize: 24
}
];
// 定义类IntroductionViewModel,获取像素介绍页面的数据
class IntroductionViewModel {
getIntroductionList() {
let introductionItems = INTRODUCE_LIST;
return introductionItems;
}
}
let introductionViewModel = new IntroductionViewModel();
export default introductionViewModel as IntroductionViewModel;
渲染像素单位介绍页面,通过ForEach循环渲染上一步构建的IntroductionViewModel的每个子菜单Item;通过if判断组件的显隐,为显示的组件,添加样式,构建像素介绍页面。 具体代码如下:
// entry/src/main/ets/pages/IntroducitonPages.ets
import IntroductionViewModel from '../viewmodel/IntroductionViewModel';
interface IntroductionItem {
name: string;
title: Resource;
subTitle: Resource;
value: string;
smallFontSize: number;
largeFontSize: number;
}
@Extend(Text) function titleTextStyle() {
.fontColor($r('app.color.title_font'))
.fontFamily('HarmonyHeiTi_Medium')
.fontWeight(500)
}
@Entry
@Component
struct IntroductionPage {
build() {
Column() {
Navigation() {
List({ space: 12 }) {
//通过ForEach循环渲染Item,构建像素介绍页面
ForEach(IntroductionViewModel.getIntroductionList(), (item: IntroductionItem) => {
//渲染每个Item
ListItem() {
Column() {
Text(item.name)
.titleTextStyle()
.fontSize('16fp')
Text(item.title)
.titleTextStyle()
.fontSize('14fp')
.fontFamily('HarmonyHeiTi')
.lineHeight('20fp')
.margin({ top: '8vp'})
.fontWeight(400)
// subTitle非空,添加Text组件,显示subTitle内容,同时添加样式;不存在则不显示
if (item.subTitle) {
Text(item.subTitle)
.titleTextStyle()
.opacity(0.6)
.lineHeight('16fp')
.fontSize('12fp')
.fontFamily($r('app.string.HarmonyHeiTi'))
.margin({ top: '20vp' })
.fontWeight(400)
}
// value非空,添加Text组件且通过宽度属性设置不同的像素单位
if (item.value.length > 0) {
Text(item.value)
.titleTextStyle()
.fontColor($r('app.color.item_background'))
.fontSize('16fp')
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.blue_background'))
.height('28vp')
.width(item.value)
.borderRadius('4vp')
.margin({ top: '12vp' })
// value为空,添加两个text组件,使用fp像素单位设置为Text组件的字体大小
} else {
Column() {
Text($r('app.string.font_desc', item.smallFontSize))
.titleTextStyle()
.fontSize(item.smallFontSize)
Text($r('app.string.font_desc', item.largeFontSize))
.titleTextStyle()
.fontSize(item.largeFontSize)
.margin({ top: '6vp' })
}
.alignItems(HorizontalAlign.Start)
.backgroundColor($r('app.color.font_background'))
.width('100%')
.borderRadius('12vp')
.padding('12vp')
.margin({ top: '12vp' })
}
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding('12vp')
.borderRadius('24vp')
.backgroundColor('#FFFFFF')
}
.padding({
left: '12vp',
right: '12vp'
})
})
}
.width('100%')
.height('100%')
}
.titleMode(NavigationTitleMode.Mini)
.title('像素介绍')
}
.backgroundColor($r('app.color.page_background'))
.width('100%')
.height('100%')
}
}
3.像素转换页面创建。 此页面主要是通过使用像素转换API,实现不同像素单位之间的相互转换功能。
- 从效果图看,此页面由3个功能相同的菜单组成,我们先构建功能菜单。创建ConversionViewModel.ets定义每个子功能菜单Item。 具体代码如下:
// entry/src/main/ets/viewmodel/ConversionViewModel.ets
// 创建自定义接口,定义每个Item中的内容
interface ConversionItem {
title: string;
subTitle: string;
value: number;
conversionTitle: string;
conversionSubTitle: string;
conversionValue: number;
notice: string;
}
// 定义类ConversionViewModel,获取像素转换页面的数据
class ConversionViewModel {
getConversionList() {
let conversionItems = CONVERSION_LIST;
return conversionItems;
}
}
// 根据自定义接口ConversionItem,填充内容
export const CONVERSION_LIST: ConversionItem[] = [
{
title: 'vp > px',
subTitle: `vp2px(60)`,
value: vp2px(60),
conversionTitle: 'px > vp',
conversionSubTitle: `px2vp(60)`,
conversionValue: px2vp(60),
notice: null
},
{
title: 'fp > px',
subTitle: `fp2px(60)`,
value: fp2px(60),
conversionTitle: 'px > fp',
conversionSubTitle: `px2fp(60})`,
conversionValue: px2fp(60),
notice: null
},
{
title: 'lpx > px',
subTitle: `lpx2px(60)`,
value: lpx2px(60),
conversionTitle: 'px > lpx',
conversionSubTitle: `px2lpx(60)`,
conversionValue: px2lpx(60),
notice: 'lpx与px之间的转换,需要根据实际情况设置designWidth'
}
];
let conversionViewModel = new ConversionViewModel();
export default conversionViewModel as ConversionViewModel;
- 渲染像素单位介绍页面,通过ForEach循环渲染上一步构建的ConversionViewModel的每个子菜单Item,同时添加样式,构建像素介绍页面。具体代码如下:
// entry/src/main/ets/pages/ConversionPage.ets
import ConversionViewModel from '../viewmodel/ConversionViewModel';
interface ConversionItem {
title: string;
subTitle: string;
value: number;
conversionTitle: string;
conversionSubTitle: string;
conversionValue: number;
notice: string;
}
@Extend(Text) function descTextStyle() {
.fontColor($r('app.color.title_font'))
.fontSize('14fp')
.fontFamily($r('app.string.HarmonyHeiTi'))
.lineHeight('20fp')
.fontWeight(400)
.margin({ top: '8vp' })
}
@Extend(Text) function titleTextStyle() {
.fontColor($r('app.color.title_font'))
.fontSize('16fp')
.fontFamily($r('app.string.HarmonyHeiTi_Medium'))
.fontWeight(500)
}
@Styles function blueStyle() {
.backgroundColor($r('app.color.blue_background'))
.height('28vp')
.borderRadius('4vp')
.margin({ top: '4vp' })
}
@Entry
@Component
struct ConversionPage {
build() {
Column() {
Navigation() {
List({ space: 12 }) {
//通过ForEach循环渲染Item,构建像素转换页面
ForEach(ConversionViewModel.getConversionList(), (item: ConversionItem) => {
//渲染每个Item
ListItem() {
Column() {
Text(item.title)
.titleTextStyle()
.margin({ top: '6vp' })
Text(item.subTitle)
.descTextStyle()
.opacity(0.6)
Row()
.blueStyle()
// 为宽度属性设置不同的像素单位
.width(item.value)
Text(item.conversionTitle)
.titleTextStyle()
.margin({ top: '18vp' })
Text(item.conversionSubTitle)
.descTextStyle()
.opacity(0.6)
Row()
.blueStyle()
// 为宽度属性设置不同的像素单位
.width(item.conversionValue)
if (item.notice) {
Text(item.notice)
.descTextStyle()
.fontColor($r('app.color.notice_font'))
}
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding('12vp')
.borderRadius('24vp')
.backgroundColor('#FFFFFF')
}
.padding({left: '12vp',right: '12vp'})
})
}
.width('100%')
.height('100%')
}
.titleMode(NavigationTitleMode.Mini)
.title('像素转换')
}
.backgroundColor($r('app.color.page_background'))
.width('100%')
.height('100%')
}
}
完整代码
本例完整代码如下:
- 应用主页面:entry/src/main/ets/pages/IndexPage.ets。
import router from '@ohos.router';
@Entry
@Component
struct IndexPage {
// 定义jumpPage方法,实现路由跳转
jumpPage(url: string) {
router.pushUrl({ url })
}
build() {
Column({ space: 24 }) {
Button('像素介绍')
.height('40vp')
.width('100%')
.backgroundColor($r('app.color.blue_background'))
// 点击时回调jumpPage方法,跳转到pages/IntroductionPage页面
.onClick(() => this.jumpPage('pages/IntroductionPage'))
Button('像素转换')
.height('40vp')
.width('100%')
.backgroundColor($r('app.color.blue_background'))
// 点击时回调jumpPage方法,跳转到pages/ConversionPage页面
.onClick(() => this.jumpPage('pages/ConversionPage'))
}
.backgroundColor($r('app.color.page_background'))
.justifyContent(FlexAlign.Center)
.padding(24)
.width('100%')
.height('100%')
}
}
- 像素介绍ViewModel:entry/src/main/ets/viewmodel/IntroducitonViewModel.ets。
// 创建自定义接口,定义每个Item中的内容
interface IntroductionItem {
name: string;
title: string;
subTitle: string;
value: string;
smallFontSize: number;
largeFontSize: number;
}
// 根据自定义接口IntroductionItem,填充内容
const INTRODUCE_LIST: IntroductionItem[] = [
{
name: 'px',
title: '屏幕物理像素单位。',
subTitle: null,
value: Constants.PIXEL_WIDTH + 'px',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'vp',
title:'屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素。',
value: Constants.PIXEL_WIDTH + 'vp',
subTitle:'像素密度为160dpi的设备上1vp=1px,1vp对应的物理屏幕像素=(屏幕像素密度/160)px',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'lpx',
title:'视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度(通过designWidth配置)的比值。',
subTitle: null,
value: Constants.PIXEL_WIDTH + 'lpx',
smallFontSize: 0,
largeFontSize: 0
},
{
name: 'fp',
title:'字体像素,与vp类似,随系统字体大小设置变化。',
subTitle:'默认情况下与vp相同,即1vp=1fp,如果用户手动调整了系统字体,scale为缩放比例,设置后的字体大小(单位fp) = 设置前的字体大小 * scale',
value: '',
smallFontSize: Constants.SMALL_FONT_SIZE,
largeFontSize: Constants.LARGE_FONT_SIZE
}
];
// 定义类IntroductionViewModel,获取像素介绍页面的数据
class IntroductionViewModel {
getIntroductionList() {
let introductionItems = INTRODUCE_LIST;
return introductionItems;
}
}
let introductionViewModel = new IntroductionViewModel();
export default introductionViewModel as IntroductionViewModel;
- 像素介绍页面:entry/src/main/ets/pages/IntroducitonPages.ets。
import IntroductionViewModel from '../viewmodel/IntroductionViewModel';
interface IntroductionItem {
name: string;
title: Resource;
subTitle: Resource;
value: string;
smallFontSize: number;
largeFontSize: number;
}
@Extend(Text) function titleTextStyle() {
.fontColor($r('app.color.title_font'))
.fontFamily('HarmonyHeiTi_Medium')
.fontWeight(500)
}
@Entry
@Component
struct IntroductionPage {
build() {
Column() {
Navigation() {
List({ space: 12 }) {
//通过ForEach循环渲染Item,构建像素介绍页面
ForEach(IntroductionViewModel.getIntroductionList(), (item: IntroductionItem) => {
//渲染每个Item
ListItem() {
Column() {
Text(item.name)
.titleTextStyle()
.fontSize('16fp')
Text(item.title)
.titleTextStyle()
.fontSize('14fp')
.fontFamily('HarmonyHeiTi')
.lineHeight('20fp')
.margin({ top: '8vp'})
.fontWeight(400)
// subTitle非空,添加Text组件,显示subTitle内容,同时添加样式;不存在则不显示
if (item.subTitle) {
Text(item.subTitle)
.titleTextStyle()
.opacity(0.6)
.lineHeight('16fp')
.fontSize('12fp')
.fontFamily($r('app.string.HarmonyHeiTi'))
.margin({ top: '20vp' })
.fontWeight(400)
}
// value非空,添加Text组件且通过宽度属性设置不同的像素单位
if (item.value.length > 0) {
Text(item.value)
.titleTextStyle()
.fontColor($r('app.color.item_background'))
.fontSize('16fp')
.textAlign(TextAlign.Center)
.backgroundColor($r('app.color.blue_background'))
.height('28vp')
.width(item.value)
.borderRadius('4vp')
.margin({ top: '12vp' })
// value为空,添加两个text组件,使用fp像素单位设置为Text组件的字体大小
} else {
Column() {
Text($r('app.string.font_desc', item.smallFontSize))
.titleTextStyle()
.fontSize(item.smallFontSize)
Text($r('app.string.font_desc', item.largeFontSize))
.titleTextStyle()
.fontSize(item.largeFontSize)
.margin({ top: '6vp' })
}
.alignItems(HorizontalAlign.Start)
.backgroundColor($r('app.color.font_background'))
.width('100%')
.borderRadius('12vp')
.padding('12vp')
.margin({ top: '12vp' })
}
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding('12vp')
.borderRadius('24vp')
.backgroundColor('#FFFFFF')
}
.padding({
left: '12vp',
right: '12vp'
})
})
}
.width('100%')
.height('100%')
}
.titleMode(NavigationTitleMode.Mini)
.title('像素介绍')
}
.backgroundColor($r('app.color.page_background'))
.width('100%')
.height('100%')
}
}
- 像素转换ViewModel:entry/src/main/ets/viewmodel/ConversionViewModel.ets。
// 创建自定义接口,定义每个Item中的内容
interface ConversionItem {
title: string;
subTitle: string;
value: number;
conversionTitle: string;
conversionSubTitle: string;
conversionValue: number;
notice: string;
}
// 定义类ConversionViewModel,获取像素转换页面的数据
class ConversionViewModel {
getConversionList() {
let conversionItems = CONVERSION_LIST;
return conversionItems;
}
}
// 根据自定义接口ConversionItem,填充内容
export const CONVERSION_LIST: ConversionItem[] = [
{
title: 'vp > px',
subTitle: `vp2px(60)`,
value: vp2px(60),
conversionTitle: 'px > vp',
conversionSubTitle: `px2vp(60)`,
conversionValue: px2vp(60),
notice: null
},
{
title: 'fp > px',
subTitle: `fp2px(60)`,
value: fp2px(60),
conversionTitle: 'px > fp',
conversionSubTitle: `px2fp(60})`,
conversionValue: px2fp(60),
notice: null
},
{
title: 'lpx > px',
subTitle: `lpx2px(60)`,
value: lpx2px(60),
conversionTitle: 'px > lpx',
conversionSubTitle: `px2lpx(60)`,
conversionValue: px2lpx(60),
notice: 'lpx与px之间的转换,需要根据实际情况设置designWidth'
}
];
let conversionViewModel = new ConversionViewModel();
export default conversionViewModel as ConversionViewModel;
- 像素转换页面:entry/src/main/ets/pages/ConversionPage.ets。
import ConversionViewModel from '../viewmodel/ConversionViewModel';
interface ConversionItem {
title: string;
subTitle: string;
value: number;
conversionTitle: string;
conversionSubTitle: string;
conversionValue: number;
notice: string;
}
@Extend(Text) function descTextStyle() {
.fontColor($r('app.color.title_font'))
.fontSize('14fp')
.fontFamily($r('app.string.HarmonyHeiTi'))
.lineHeight('20fp')
.fontWeight(400)
.margin({ top: '8vp' })
}
@Extend(Text) function titleTextStyle() {
.fontColor($r('app.color.title_font'))
.fontSize('16fp')
.fontFamily($r('app.string.HarmonyHeiTi_Medium'))
.fontWeight(500)
}
@Styles function blueStyle() {
.backgroundColor($r('app.color.blue_background'))
.height('28vp')
.borderRadius('4vp')
.margin({ top: '4vp' })
}
@Entry
@Component
struct ConversionPage {
build() {
Column() {
Navigation() {
List({ space: 12 }) {
//通过ForEach循环渲染Item,构建像素转换页面
ForEach(ConversionViewModel.getConversionList(), (item: ConversionItem) => {
//渲染每个Item
ListItem() {
Column() {
Text(item.title)
.titleTextStyle()
.margin({ top: '6vp' })
Text(item.subTitle)
.descTextStyle()
.opacity(0.6)
Row()
.blueStyle()
// 为宽度属性设置不同的像素单位
.width(item.value)
Text(item.conversionTitle)
.titleTextStyle()
.margin({ top: '18vp' })
Text(item.conversionSubTitle)
.descTextStyle()
.opacity(0.6)
Row()
.blueStyle()
// 为宽度属性设置不同的像素单位
.width(item.conversionValue)
if (item.notice) {
Text(item.notice)
.descTextStyle()
.fontColor($r('app.color.notice_font'))
}
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding('12vp')
.borderRadius('24vp')
.backgroundColor('#FFFFFF')
}
.padding({left: '12vp',right: '12vp'})
})
}
.width('100%')
.height('100%')
}
.titleMode(NavigationTitleMode.Mini)
.title('像素转换')
}
.backgroundColor($r('app.color.page_background'))
.width('100%')
.height('100%')
}
}
我这边特意整理了《鸿蒙语法ArkTS、TypeScript、ArkUI、实战开发视频教程》以及《鸿蒙生态应用开发白皮书V2.0PDF》《鸿蒙开发学习手册》(共计890页)鸿蒙开发资料等…希望对大家有所帮助:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
鸿蒙语法ArkTS、TypeScript、ArkUI等…视频教程:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
OpenHarmony APP开发教程步骤:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
鸿蒙生态应用开发白皮书V2.0PDF:https://docs.qq.com/doc/DZVVkRGRUd3pHSnFG
应用开发中级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
应用开发中高级就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
南北双向高工技能基础:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
全网首发-工业级 南向设备开发就业技术:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
《鸿蒙开发学习手册》:
如何快速入门:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发:https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……