1# 短视频切换案例 2 3### 介绍 4 5本示例介绍了[@ohos.multimedia.media](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-media-kit/js-apis-media.md)组件和[@ohos.window](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-arkui/js-apis-window.md)接口以及使用[触摸热区](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-universal-attributes-touch-target.md)实现视频横竖屏切换及进度条热区拖动的功能。 6该场景多用于横竖屏视频等媒体播放。 7 8### 效果图预览 9 10<img src="./entry/src/main/resources/base/media/video_screen_direction_switching.gif" width="300"> 11 12**使用说明**: 13 14* 点击全屏观看按钮,切换横屏窗口。 15* 点击左上角返回按钮,恢复竖屏窗口。 16* 在进度条上方位置横向拖动可带动进度条移动。 17 18## 实现步骤 19 201. 初始化[@ohos.multimedia.media](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-media-kit/js-apis-media.md)的AVPlayer。 21 ```ts 22 async Init(): Promise<void> { 23 await this.release(); 24 const context = getContext(this); 25 // 获取fdSrc用于注册AVPlayer 26 context.resourceManager.getRawFd(this.fileName).then(async (value: resourceManager.RawFileDescriptor) => { 27 this.avPlayer = await media.createAVPlayer(); 28 this.isCreate = true; 29 this.setSourceInfo(); // 视频信息上报函数 30 this.setStateChangeCallback(); // 状态机上报回调函数 31 this.avPlayer.fdSrc = { 32 fd: value.fd, 33 offset: value.offset, 34 length: value.length 35 }; 36 }); 37 } 38 ``` 392. 当AVPlayer初始化完毕进入initialized状态时,将XComponent和AVPlayer通过surfaceId绑定,这样可以在XComponent组件内实现视频播放功能。比起Video组件,AVPlayer可以更方便自定义全屏动画效果。 40 ```ts 41 // TODO 知识点:XComponent和AVPlayer通过surfaceId绑定 42 setSurfaceID(): void { 43 logger.info('play video: surfaceID is:' + this.surfaceID); 44 this.avPlayer.surfaceId = this.surfaceID; 45 } 46 ``` 473. 使用[AVPlayer](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-media-kit/js-apis-media.md#avplayer9)中的width和height属性判断是横屏视频还是竖屏视频,方便判断是否需要展示**全屏观看**按钮。 48```ts 49 case 'prepared': 50 logger.info('state prepared called'); 51 this.isPlaying = true; // 准备完成阶段 开始播放 52 this.totalDuration = this.avPlayer.duration; // 获取视频时长 53 this.aspect_ratio = this.avPlayer.width / this.avPlayer.height; // 获取视频宽高比 54 if(this.avPlayer.width >= this.avPlayer.height) { // 判断是横屏视频还是竖屏视频 55 this.verticalVideo = false; 56 } else { 57 this.verticalVideo = true; 58 } 59 this.getPlay(); 60 break; 61``` 624. 调用[@ohos.window](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-arkui/js-apis-window.md)的 getLastWindow 方法获取当前应用内最上层的子窗口,若无应用子窗口,则返回应用主窗口。 635. 利用获取到的窗口对象,调用 setWindowSystemBarEnable 方法设置窗口是否显示导航栏和状态栏。 646. 调用窗口对象的 setPreferredOrientation 方法设置窗口旋转方向以及是否应用重力感应。 657. 调用窗口对象的setWindowLayoutFullScreen方法实现沉浸式布局。 66 ```ts 67 changeOrientation() { 68 // 获取UIAbility实例的上下文信息 69 let context = getContext(this); 70 // 调用该接口手动改变设备横竖屏状态(设置全屏模式,先强制横屏,再加上传感器模式) 71 window.getLastWindow(context).then((lastWindow) => { 72 if (this.isLandscape) { 73 // 设置窗口的布局是否为沉浸式布局 74 lastWindow.setWindowLayoutFullScreen(true, () => { 75 // 设置窗口全屏模式时导航栏、状态栏的可见模式 76 lastWindow.setWindowSystemBarEnable([]); 77 // 设置窗口的显示方向属性,AUTO_ROTATION_LANDSCAPE表示传感器自动横向旋转模式 78 lastWindow.setPreferredOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE); 79 }); 80 } else { 81 // 设置窗口的显示方向属性,UNSPECIFIED表示未定义方向模式,由系统判定 82 lastWindow.setPreferredOrientation(window.Orientation.UNSPECIFIED, () => { 83 // 设置窗口全屏模式时导航栏、状态栏的可见模式 84 lastWindow.setWindowSystemBarEnable(WINDOW_SYSTEM_BAR, () => { 85 // 设置窗口的布局是否为沉浸式布局 86 lastWindow.setWindowLayoutFullScreen(false, () => { 87 setTimeout(() => { 88 // 设置退出全屏动画 89 animateTo({ 90 duration: ANIMATE_DURATION, 91 onFinish: () => { 92 this.fileName = ''; 93 } 94 }, () => { 95 this.isFullScreen = !this.isFullScreen; 96 }); 97 }, TIMEOUT_DURATION); 98 }); 99 }); 100 }); 101 } 102 }); 103 } 104 ``` 1058. 对进度条整个组件设置[触摸热区](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-arkui/arkui-ts/ts-universal-attributes-touch-target.md)。 106 ```ts 107 Row() { 108 // ... 109 Slider({ 110 value: Math.round(this.currentTime / this.totalDuration * 100) 111 }) 112 .onChange((value: number, mode: SliderChangeMode) => { 113 if (this.isCreate) { 114 this.currentTime = this.totalDuration * value / 100; 115 this.avPlayer.seek(this.currentTime); 116 if (mode === SliderChangeMode.Moving) { 117 if (this.avPlayer.state === 'playing') { 118 this.getPause(); 119 } 120 } else if (mode === SliderChangeMode.End) { 121 if (this.avPlayer.state === 'paused' && this.isPlaying) { 122 this.getPlay(); 123 } 124 } 125 } 126 }) 127 // ... 128 } 129 .hitTestBehavior(HitTestMode.Transparent) // 将组件的触摸测试类型设置为自身和子节点都响应触摸测试,不会阻塞兄弟节点的触摸测试,不会影响祖先节点的触摸测试。 130 .responseRegion( // 设置多个触摸热区 131 [ 132 { 133 x: 0, 134 y: 0, 135 width: $r('app.string.video_screen_direction_switching_layout_100'), 136 height: $r('app.string.video_screen_direction_switching_layout_100') 137 }, 138 { 139 x: 0, 140 y: $r('app.string.video_screen_direction_switching_layout_negative_200'), 141 width: $r('app.string.video_screen_direction_switching_layout_100'), 142 height: $r('app.string.video_screen_direction_switching_layout_200') 143 } 144 ] 145 ) 146 .gesture( // 设置拖动手势,将距离变量与进度条变量进行绑定计算 147 PanGesture(new PanGestureOptions({ 148 direction: PanDirection.Left | PanDirection.Right 149 })) 150 .onActionStart(() => { 151 this.flagValue = this.currentTime; 152 }) 153 .onActionUpdate((event?: GestureEvent) => { 154 if (event) { 155 if (this.isCreate) { 156 if (this.avPlayer.state === 'playing') { 157 this.getPause(); 158 } 159 this.currentTime = (this.flagValue + this.totalDuration * (event.offsetX / 3) / 100) > this.totalDuration ? this.totalDuration : (this.flagValue + this.totalDuration * (event.offsetX / 3) / 100); 160 } 161 } 162 }) 163 .onActionEnd(() => { 164 if (this.isCreate) { 165 this.avPlayer.seek(this.currentTime); 166 if (this.avPlayer.state === 'paused' && this.isPlaying) { 167 this.getPlay(); 168 } 169 } 170 }) 171 ) 172 ``` 173 174### 高性能知识点 175 1761. 本示例使用了[LazyForEach](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/quick-start/arkts-rendering-control-lazyforeach.md) 进行数据懒加载优化,以降低内存占用和渲染开销。 177 178### 工程结构&模块类型 179 180 ``` 181 VideoSwitching // hap 182 |---component 183 | |---VideoComponent.ets // AVPlayer组件页面 184 |---model 185 | |---BasicDataSource.ets // 数据类型文件 186 |---pages 187 | |---Index.ets // 入口文件 188 |---util 189 | |---TimeTools.ets // 时间轴组件页面 190 |---view 191 | |---VideoScreenDirectionSwitching.ets // 视频横竖屏切换容器页面 192 ``` 193 194### 参考资料 195 196[LazyForEach](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/quick-start/arkts-rendering-control-lazyforeach.md) 197 198[@ohos.multimedia.media](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-media-kit/js-apis-media.md) 199 200[@ohos.window](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.0-Release/zh-cn/application-dev/reference/apis-arkui/js-apis-window.md) 201 202### 约束与限制 203 2041.本示例仅支持在标准系统上运行。 205 2062.本示例仅支持API12版本SDK,版本号:5.0.0.71。 207 2082.本示例需要使用DevEco Studio 5.0.0 Release 才可编译运行。 209 210### 下载 211 212如需单独下载本工程,执行如下命令: 213```typescript 214git init 215git config core.sparsecheckout true 216echo /code/BasicFeature/Media/VideoSwitching/ > .git/info/sparse-checkout 217git remote add origin https://gitee.com/openharmony/applications_app_samples.git 218git pull origin master 219```