【鸿蒙性能优化】横竖屏切换开发实践

打印 上一主题 下一主题

主题 976|帖子 976|积分 2928

往期知识点整理



  • 鸿蒙(HarmonyOS)北向开发知识点记载~
  • 【鸿蒙性能优化】基于ArkUI的冷启动加载完成时延问题分析思路&案例
  • 【鸿蒙性能优化】基于List的滑动过程卡顿率问题分析&案例
  • 【鸿蒙性能优化】基于ArkUI启动冷启动过程最大连续丢帧数问题分析思路&案例
  • 【鸿蒙性能优化】冷启动响应时延问题分析思路&案例
  • 【鸿蒙性能优化】横竖屏切换开发实践
  • 持续更新中……
实现视频播窗页面的横竖屏转换,用户可以切换到全屏播放场景。
Figure1横竖屏切换表示

团体开发基本流程

以进入全屏为例,开发过程必要有以下关注点:
1、如果有模态框,必要关闭模态框
2、页面实现窗口旋转和全屏逻辑
3、隐藏播控相关按钮内容
图1进入横屏基本流程

在触发旋转的按钮上,触发对应变乱,并可以接纳EventHub.emit发送对应变乱触发相应变乱。这里重要先容进入全屏的逻辑。
  1. @Builder
  2. RotationButton() {
  3.   Column() {
  4.     Image($r('app.media.ic_rotate'))
  5.       .width(22).height(22)
  6.   }
  7.   ...
  8.   .onClick(() => {
  9.     this.context.eventHub.emit(DATA_EVENT_LIST.CLOSE_BIND_SHEET);
  10.     this.context.eventHub.emit(PLAY_EVENT_LIST.ENTRY_FULL);
  11.     this.closeMenu();
  12.   })
  13. }
复制代码
进入全屏逻辑

1、实现窗口旋转

重要逻辑包括:(1)获取当前的方向属性值 (2)调用窗口管理的接口设置想要的旋转方向 (3)隐藏状态栏
实行进入全屏时首先必要通过display获取当前的显示方向,并根据当前显示方向,实行对应的屏幕旋转逻辑。
  1. import { display , window } from '@kit.ArkUI';
  2. // 通过display的接口获取方向属性值,display中的方向与window中的方向相同名称,值相差1
  3. const orientation = display.getDefaultDisplaySync().orientation + 1;
  4. // 以直板机为例设置旋转方向
  5. if (orientation === window.Orientation.PORTRAIT) {
  6.   // 竖屏模式切全屏需要转屏,横屏模式切换全屏不需要转屏
  7.   this.setOrientation(window.Orientation.LANDSCAPE_INVERTED)
  8. } else if (orientation === window.Orientation.PORTRAIT_INVERTED) {
  9.   this.setOrientation(window.Orientation.LANDSCAPE)
  10. }
  11. // 设置屏幕旋转方向
  12. setOrientation(orientation: number) {
  13.   Log.info(TAG, `[setOrientation]orientation: ${orientation}`);
  14.   WindowAbility.setWindowOrientation(WindowAbility.getWindowStage(), this.context, orientation)
  15. }
  16. // 隐藏状态栏等业务处理……
  17. hideSystemBar() {
  18.   WindowUtil.hideSystemBar(WindowUtil.getWindowStage())
  19. }
复制代码
具体的实现逻辑在windowUtil中,在调用屏幕旋转方向时,可以通过windowStage拿到对应的window实例,并设置旋转setPreferredOrientation。
  1. import { window } from '@kit.ArkUI';
  2. export class WindowUtil {
  3.   private static windowStage: window.WindowStage;
  4.   static getWindowStage() {
  5.     return WindowUtil.windowStage
  6.   }
  7.   static setWindowStage(windowStage: window.WindowStage): void {
  8.     WindowUtil.windowStage = windowStage;
  9.   }
  10.   /*
  11.     * 设置横竖屏
  12.     * @Params orientation: 旋转方向
  13.     */
  14.   static setWindowOrientation(windowStage: window.WindowStage, orientation: number): void {
  15.     windowStage.getMainWindow((error, win: window.Window) => {
  16.       win.setPreferredOrientation(orientation).then((data) => {
  17.         // do some log
  18.       }).catch((err: Error) => {
  19.       });
  20.     })
  21.   }
  22.   /**
  23.    * 设置隐藏状态栏
  24.    * @param windowStage
  25.    */
  26.   static hideSystemBar (windowStage: window.WindowStage) {
  27.     windowStage.getMainWindow((error, win: window.Window) => {
  28.       win.setWindowSystemBarEnable([])
  29.     })
  30.   }
  31. }
复制代码
对于如果必要跟随系统传感器一起旋转可以举行如下设置:
  1. window.getLastWindow(getContext(this)).then((win) => {
  2.   win.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED)
  3. })
复制代码
2、监听窗口尺寸变革

界面中根据调用窗口实例的.on方法,监听窗口尺寸的变革,在尺寸变革触发回调函数中,实行相应的尺寸修改逻辑:

  • window.Size拿到的尺寸是px,必要转换为vp。
  • isEntryFull作为标志位,根据标志位实行进入全屏和退出全屏的逻辑。
  • 以进入全屏为例,必要拿到当前窗口的尺寸,并保留改值到viewHeight和viewWidth,对应视频播放组件的宽高。
  1. const aspect = 9 / 16; // 播窗长宽比16:9
  2. const aspectWindow = 2 / 3; // 横屏页面布局2:1
  3. // 拿到窗口实例并执行监听窗口尺寸变化事件
  4. WindowAbility.getWindowStage().getMainWindowSync().on('windowSizeChange', (size: window.Size) => {
  5.   this.screenChange(size);
  6. });
  7. screenChange(size:window.Size) {
  8.   let viewWidth = px2vp((Number(size.width)));
  9.   let viewHeight = px2vp((Number(size.height)));
  10.   if (this.isEntryFull) { // 全屏时,宽高为窗口的宽高
  11.     this.isFull = true;
  12.     this.viewWidth = viewWidth;
  13.     this.viewHeight = viewHeight;
  14.   } else {
  15.     this.isFull = false;
  16.     if (viewWidth < limitWidth) { // 非全屏且小于840时,高度为宽度的9/16
  17.       this.viewHeight = viewWidth * aspect;
  18.       this.viewWidth = viewWidth;
  19.     } else { // 12栅格下(例如pad),窗口高度去掉状态栏高度
  20.       this.viewHeight = viewHeight - px2vp(this.topRectHeight);
  21.       this.viewWidth = viewWidth * aspectWindow;
  22.     }
  23.   }
  24. }
复制代码
3、通过状态变量触发视频组件更新

根据之前拿到的viewHeight和viewWidth,在页面上作为状态变量在组件上使用。即可完成视频播放组件的窗口全屏功能开发。
这里必要注意由于页面进入全屏时是进入了沉浸式,播窗的高度必要思量状态栏高度。
  1. // 获取状态栏高度
  2. let type = window.AvoidAreaType.TYPE_SYSTEM;
  3. let avoidArea = windowClass.getWindowAvoidArea(type);
  4. let topRectHeight= avoidArea.topRect.height;
复制代码
在组件上使用计算后的高度和宽度。
  1. // 由于页面是沉浸式,播窗容器高度需要考虑信号栏高度
  2. getContentHeight() {
  3.   return this.isFull ? this.viewHeight : (this.viewHeight) + px2vp(this.topRectHeight)
  4. }
  5. build() {
  6.   Stack({ alignContent: Alignment.TopStart }) {
  7.     OnlinePlayer({
  8.       isFull: this.isFull,
  9.       viewWidth: this.viewWidth, // 传入OnlinePlayer,对播窗内容xcomponent设置宽高
  10.       viewHeight: this.viewHeight,
  11.     })
  12.   }
  13.   .width(this.viewWidth) // 设置播窗容器的宽高
  14.   .height(this.getContentHeight())
  15. }
复制代码
性能优化操作

1. 冻结其他不相关页面
如果在旋转的页面以外存在其他页面没有被销毁,并且监听了窗口变革等变乱,会接收到变乱并触发状态变量的更新和组件革新。此时必要使用ArkUI的冻结能力,将页面配置冻结属性,使其在不可见页面的时候不会触发它及子组件状态变量的更新。

2. 淘汰零宽字符
在旋转过程绘制中,由于生成文字缓存非常耗时图形所以会把文字转成图片,但是如果该页面上的文字使用了零宽字符,会导致每个字符被转成图片,会带来额外的绘制负载。该场景可以使用word-break属性替代。

3. 对图片使用AutoResize属性
如果当前旋转页面存在一些图片,未经合理的裁剪,图片过大,可以对图片设置AutoResize属性,使图片裁剪到合适的大小举行绘制。

4. 检查一些耗时操作
排查当前页面是否存在一些冗余的OnAreaChange变乱、含糊或者一些线性变革linearGradient的属性,这些都比力耗时,如果存在看是否可以举行优化。
简化后的示例代码

以下是简化后的示例代码,可直接运行参考使用
  1. import { window } from '@kit.ArkUI';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   @State show:boolean = true;
  6.   // XComponent的控制器
  7.   private mXComponentController: XComponentController = new XComponentController();
  8.   // XComponent宽度
  9.   @State xComponentWidth:number|string = 0;
  10.   // XComponent高度
  11.   @State xComponentHeight:number|string = 0;
  12.   @State isFull:boolean = false;// 默认非全屏
  13.   // 设置窗口方向
  14.   setR(orientation:number){
  15.     window.getLastWindow(getContext(this)).then((win) => {
  16.       win.setPreferredOrientation(orientation).then((data) => {
  17.         console.log('setWindowOrientation: '+orientation+' Succeeded. Data: ' + JSON.stringify(data));
  18.       }).catch((err:string) => {
  19.         console.log('setWindowOrientation: Failed. Cause: ' + JSON.stringify(err));
  20.       });
  21.     }).catch((err:string) => {
  22.       console.log( 'setWindowOrientation: Failed to obtain the top window. Cause: ' + JSON.stringify(err));
  23.     });
  24.   }
  25.   aboutToAppear(){
  26.     this.xComponentWidth = '100%';
  27.     this.xComponentHeight = '40%';
  28.     window.getLastWindow(getContext(this)).then((win) => {
  29.       // 监听屏幕尺寸变化,变化后修改XComponent的宽高
  30.       win.on('windowSizeChange', (size) => {
  31.         console.log('windowSizeChange:'+JSON.stringify(size));
  32.         // 全屏时宽高占满屏幕
  33.         if(this.isFull){
  34.           this.xComponentWidth = px2vp(size.width);
  35.           this.xComponentHeight = px2vp(size.height);
  36.         }else{
  37.           // 非全屏时宽度100%,高度40%
  38.           this.xComponentWidth = px2vp(size.width);
  39.           this.xComponentHeight = '40%';
  40.         }
  41.       })
  42.     })
  43.   }
  44.   playVideo(){
  45.   }
  46.   build() {
  47.     Stack({ alignContent: Alignment.TopStart }){
  48.       Row() {
  49.         XComponent({
  50.           id: 'componentId',
  51.           type: 'surface',
  52.           controller: this.mXComponentController
  53.         })
  54.           .width(this.xComponentWidth)
  55.           .height(this.xComponentHeight)
  56.           .backgroundColor(Color.Black)
  57.           .onLoad(() => {
  58.             this.playVideo();
  59.           })
  60.       }
  61.       Button('横竖屏切换').onClick(()=>{
  62.         // 全屏时,横变竖
  63.         if(this.isFull){
  64.           this.setR(1);
  65.           this.isFull = false;
  66.         }else{// 非全屏时,竖变横
  67.           this.isFull = true;
  68.           this.setR(4);
  69.         }
  70.       }).position({x:0,y:0}).backgroundColor(Color.Green)
  71.     }.width('100%').height('100%').backgroundColor(Color.Red)
  72.   }
  73. }
复制代码
总是有很多小伙伴反馈说:鸿蒙开发不知道学习哪些技能?不知道必要重点掌握哪些鸿蒙开发知识点? 为了解决各人这些学习烦恼。在这准备了一份很实用的鸿蒙全栈开发学习门路与学习文档给各人用来跟着学习。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技能的学习门路,包罗了鸿蒙开发必掌握的核心知识要点,内容有(OpenHarmony多媒体技能、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植……等)技能知识点。

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发底子知识:

1.应用底子知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私掩护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共变乱与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发口试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview


OpenHarmony 开发情况搭建


《OpenHarmony源码剖析》:https://gitcode.com/HarmonyOS_MN/733GH/overview



  • 搭建开发情况
  • Windows 开发情况的搭建
  • Ubuntu 开发情况搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通讯子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

千千梦丶琪

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表