openHarmony的UI开发
自适应布局
拉伸能力
Blank在容器主轴方向上,空白填充组件具有自动填充容器空余部分的能力。仅当父组件为Row/Column时生效,即是线性布局。这样便可以在两个固定宽度或高度的组件中间添加一个Blank(),将剩余空间占满,从而实现占两头的效果
而在使用弹性布局中,可以使用flexBasis设置子组件在父容器主轴方向上的基准尺寸,还可以使用flexGrow来设置父容器的剩余空间分配给此属性所在组件的比例。最后还有flexShrink,它可以在父容器空间不足时,压缩子组件的比例。
缩放能力
自适应缩放是指在各种不同大小设备中,子组件按照预设的比例,尺寸随容器尺寸的变化而变化。有两种方法可以实现。
1、使用layoutWeight属性
父容器尺寸确定时,设置了layoutWeight属性的子组件与兄弟元素占主轴尺寸按照权重进行分配,忽略元素本身尺寸设 置,在任意尺寸设备下,自适应占满剩余空间
2、使用百分比设置
父容器尺寸确定时,使用百分比设置子组件以及兄弟组件的width宽度,可以保证各自元素在任意尺寸下的自适应占比
定位能力
1、相对定位
使用组件的offset属性进行相对定位,会根据初始位置按照设定好的偏移量进行位置调整。使用线性布局和offset可以实现大部 分布局的开发
2、绝对定位
使用positon属性实现绝对布局,设定左上角为(0,0)位置,可以设置x和y坐标进行位置调整。但是对于不同尺寸的设备,使 得很多时候用绝对定位的适应性会比较差,在屏幕的适配上有缺陷
延伸能力
容器在一个屏幕下无法展示所有内容的时候,就需要通过滚动条拖动展示,方法有两种:List组件和Scroll组件。list和scroll纵向 和横向均可以。用循环渲染进行渲染,若数据量过多,超过了一个屏幕,便可以通过滚动条拖动显示。
均分能力
线性布局
通过上图的三个参数设置,实现了布局的自适应均分能力
弹性布局
相比于前者,弹性布局少了space参数,但是direction、wrap和alignContent的加入,更加能体现出布局的自适应均分能力
网格布局
网格布局具备较强的页面均分能力,它可以按行按列将容器均分成n块,然后再按列按行将子组件一个个放进去。例如在计算器 应用,可以将网格布局的页面均分能力充分地体现出来。
占比能力
网格布局具备较强的子组件占比控制能力,支持自定义网格布局行数和列数,以及每行每列尺寸占比,支持设置子组件横跨几行 或者几列
隐藏能力
1、条件渲染
2、显隐控制
拆行能力
网格布局支持设置子组件横跨几行或者几列
响应式布局
断点
栅格系统断点
栅格系统通过监听窗口或容器的尺寸变化进行断点,通过reference设置断点切换参考物,从而实现了根据断点来判断采用哪 种设备的布局设置,最多可以设置六种设备。
{ // 设栅格系统有12列,断点有五个,可以判断六种设备columns: 12, breakpoints: { value: ['200vp', '300vp', '400vp', '500vp', '1200vp'], reference: BreakpointsReference.ComponentSize } span: { // 第一个种设备每个栅格子元素占用两列,第二种3列,第三种四列,以此类推 xs: 2, sm: 3, md: 4, lg: 6, xl: 8, xxl: 12}
上面参数的设置的效果如下图所示:
媒体查询
媒体查询(Media Query)作为响应式设计的核心,在移动设备上应用十分广泛。它根据不同设备类型或同设备不同状态修改应用的样式。媒体查询的优势有:
- 提供丰富的媒体特征监听能力,针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。
- 当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。
官方例子:
import mediaquery from '@ohos.mediaquery'let portraitFunc = null@Entry@Componentstruct MediaQueryExample { @State color: string = '#DB7093' @State text: string = 'Portrait' listener = mediaquery.matchMediaSync('(orientation: landscape)') // 当设备横屏时条件成立 onPortrait(mediaQueryResult) { if (mediaQueryResult.matches) { this.color = '#FFD700' this.text = 'Landscape' } else { this.color = '#DB7093' this.text = 'Portrait' } } aboutToAppear() { portraitFunc = this.onPortrait.bind(this) // 绑定当前应用实例 this.listener.on('change', portraitFunc) } build() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Text(this.text).fontSize(50).fontColor(this.color) } .width('100%').height('100%') }}
横屏下文本内容为Landscape,颜色为#FFD700
非横屏下文本内容为Portrait,颜色为#DB7093
栅格布局
栅格布局的总列数可以参考断点处,就是要设置好一共有多少行,还有每个栅格子元素占用多少行
栅格子组件间距也很简单,就设置一下gutter就可以了,gutter: 10是水平间距,gutter: { x: 20, y: 50 } 是水平垂直间距
排列方向有从左往右和从右往左
而对于栅格子组件GridCol,有span,offset,order三个参数可以设置
span:子组件占栅格布局的列数,可以统一设置,也可以分设备类型设置不同占比
offset:就是偏移列数,就是可以起到拆列的作用
order:栅格子组件的序号,决定子组件排列次序。当子组件不设置order或者设置相同的order, 子组件按照代码顺序展示。当子组件设置不同的order时,order较小的组件在前,较大的在后。 当子组件部分设置order,部分不设置order时,未设置order的子组件依次排序靠前,设置了order的子组件按照数值从小到大排列。
动画
属性动画
这个可以直接跟在组件的属性方法里面进行填写,不过只支持width、height、backgroundColor、opacity、scale、rotate、translate等部分通用属性变化时生效
.animation({ duration: 2000, curve: Curve.EaseOut, iterations: 3, playMode: PlayMode.Normal })
显式动画
提供全局animateTo显式动画接口来指定由于闭包代码导致的状态变化插入过渡动效。
和前者不同的是,这里可以在全局对设定好的参数进行改变便可以触发动画的执行
.onClick(() => { if (this.flag) { animateTo({duration: 2000,curve: Curve.EaseOut,iterations: 3,playMode: PlayMode.Normal,onFinish: () => { console.info('play end')} }, () => {this.widthSize = 100this.heightSize = 50 }) } else { animateTo({}, () => {this.widthSize = 250this.heightSize = 100 }) } this.flag = !this.flag })
转场动画
页面间转场
在全局pageTransition方法内配置页面入场和页面退场时的自定义转场动效。
官方例子:配置了当前页面的入场动画为淡入,退场动画为缩小。
// index.ets@Entry@Componentstruct PageTransitionExample1 { @State scale1: number = 1 @State opacity1: number = 1 build() { Column() { Navigator({ target: 'pages/page1', type: NavigationType.Push }) { Image($r('app.media.bg1')).width('100%').height('100%') // 图片存放在media文件夹下 } }.scale({ x: this.scale1 }).opacity(this.opacity1) } // 自定义方式1:完全自定义转场过程的效果 pageTransition() { PageTransitionEnter({ duration: 1200, curve: Curve.Linear }) .onEnter((type: RouteType, progress: number) => { this.scale1 = 1 this.opacity1 = progress }) // 进场过程中会逐帧触发onEnter回调,入参为动效的归一化进度(0% -- 100%) PageTransitionExit({ duration: 1500, curve: Curve.Ease }) .onExit((type: RouteType, progress: number) => { this.scale1 = 1 - progress this.opacity1 = 1 }) // 退场过程中会逐帧触发onExit回调,入参为动效的归一化进度(0% -- 100%) }}
// page1.ets@Entry@Componentstruct AExample { @State scale2: number = 1 @State opacity2: number = 1 build() { Column() { Navigator({ target: 'pages/index', type: NavigationType.Push }) { Image($r('app.media.bg2')).width('100%').height('100%') // 图片存放在media文件夹下 } }.width('100%').height('100%').scale({ x: this.scale2 }).opacity(this.opacity2) } // 自定义方式1:完全自定义转场过程的效果 pageTransition() { PageTransitionEnter({ duration: 1200, curve: Curve.Linear }) .onEnter((type: RouteType, progress: number) => { this.scale2 = 1 this.opacity2 = progress }) // 进场过程中会逐帧触发onEnter回调,入参为动效的归一化进度(0% -- 100%) PageTransitionExit({ duration: 1500, curve: Curve.Ease }) .onExit((type: RouteType, progress: number) => { this.scale2 = 1 - progress this.opacity2 = 1 }) // 退场过程中会逐帧触发onExit回调,入参为动效的归一化进度(0% -- 100%) }}
组件内转场
组件内转场主要通过transition属性配置转场参数,在组件插入和删除时显示过渡动效,主要用于容器组件中的子组件插入和删除时,提升用户体验(需要配合animateTo才能生效,动效时长、曲线、延时跟随animateTo中的配置)。
官方实例:点击消失时,在预览器中无法看到生效效果,但其实已经生效了,需要在开发板或真机中才能看到
// xxx.ets@Entry@Componentstruct TransitionExample { @State flag: boolean = true @State show: string = 'show' build() { Column() { Button(this.show).width(80).height(30).margin(30) .onClick(() => { // 点击Button控制Image的显示和消失 animateTo({ duration: 1000 }, () => { if (this.flag) {this.show = 'hide' } else {this.show = 'show' } this.flag = !this.flag }) }) if (this.flag) { // Image的显示和消失配置为不同的过渡效果 Image($r('app.media.testImg')).width(300).height(300) .transition({ type: TransitionType.Insert, scale: { x: 0, y: 1.0 } }) .transition({ type: TransitionType.Delete, rotate: { angle: 180 } }) } }.width('100%') }}
共享元素转场
设置页面间转场时共享元素的转场动效
官方实例:点击图片跳转页面时,显示共享元素图片的自定义转场动效。
// xxx.ets@Entry@Componentstruct SharedTransitionExample { @State active: boolean = false build() { Column() { Navigator({ target: 'pages/PageB', type: NavigationType.Push }) { Image($r('app.media.ic_health_heart')).width(50).height(50) .sharedTransition('sharedImage', { duration: 800, curve: Curve.Linear, delay: 100 }) }.padding({ left: 20, top: 20 }) .onClick(() => { this.active = true }) } }}
// PageB.ets@Entry@Componentstruct pageBExample { build() { Stack() { Image($r('app.media.ic_health_heart')).width(150).height(150) .sharedTransition('sharedImage', { duration: 800, curve: Curve.Linear, delay: 100 }) }.width('100%').height('100%') }}
路径动画
设置组件进行位移动画时的运动路径
toggle作为参数,当它改变的时候,组件的位置会发生改变从而触发了动画的执行,而motionPath可以将它的路径规划出来。
弹窗
警告弹窗
显示警告弹窗组件,可设置文本内容与响应回调
里面可以填写一些与用户交互的信息,并通过确认或取消按钮触发一个回调,从而完成一定的目的
列表选择弹窗
里面有可滑动选择的信息,并通过确认或取消按钮触发一个回调,从而完成一定的目的
自定义弹窗
通过CustomDialogController类显示自定义弹窗。使用弹窗组件时,可优先考虑自定义弹窗,便于自定义弹窗的样式与内容。
这可以给开发者自定义更多弹窗样式,开发出弹窗更加多的功能
日期滑动选择器弹窗
根据指定的日期范围创建日期滑动选择器,展示在弹窗上。
时间滑动选择器弹窗
以24小时的时间区间创建时间滑动选择器,展示在弹窗上。
文本滑动选择器弹窗
根据指定的选择范围创建文本选择器,展示在弹窗上。
手势处理
绑定手势方法
为组件绑定不同类型的手势事件,并设置事件的响应方法。
绑定方式有三种,gesture绑定手势,priorityGesture绑定优先识别手势,parallelGesture绑定可与子组件手势同时触发的手势(手势事件为非冒泡事件,通过这个可以改成冒泡事件)。另外,可以通过更改GestureMask的参数来选择是否屏蔽掉子组件的手势。最后,onAction为响应手势事件,即为Tap手势识别成功回调。
手势类型有七种
名称 | 描述 |
---|---|
TapGesture | 点击手势,支持单次点击、多次点击识别。 |
LongPressGesture | 长按手势。 |
PanGesture | 平移手势,滑动最小距离为5vp时识别成功。 |
PinchGesture | 捏合手势。 |
RotationGesture | 旋转手势。 |
SwipeGesture | 滑动手势,滑动最小速度为100vp/s时识别成功。 |
GestureGroup | 手势识别组,多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。 |
每种手势类型都有一个fingers参数用来限制可以几指触发,另外还有各种独有的参数,有direction,speed,angle,distance,direction,repeat,repeat
组合手势
手势识别组合,即多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。
用mode设置组合手势识别模式,有顺序识别、并发识别和互斥识别
s时识别成功。 |
| GestureGroup | 手势识别组,多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。 |
每种手势类型都有一个fingers参数用来限制可以几指触发,另外还有各种独有的参数,有direction,speed,angle,distance,direction,repeat,repeat
组合手势
手势识别组合,即多种手势组合为复合手势,支持连续识别、并行识别和互斥识别。
用mode设置组合手势识别模式,有顺序识别、并发识别和互斥识别