鸿蒙应用框架开发【画中画效果实现】 UI框架

server/2024/12/22 9:08:51/

画中画效果实现

介绍

本示例通过@kit.ArkUI、@kit.MediaKit等接口,实现了视频播放、手动和自动拉起画中画、画中画窗口控制视频播放和暂停等功能。

效果预览

1

使用说明

  1. 在主界面,可以点击对应视频按钮进入视频播放页面
  2. 视频播放页面点击开启,应用拉起画中画,点击关闭关闭画中画
  3. 视频播放页面点击自动开启画中画,在返回桌面时会自动拉起画中画
  4. 视频播放页面会显示一些回调信息

具体实现

  • 整个示例用Navigation构建页面,主页面放置五个可点击视频框,点击之后进入视频播放页面。
  • 进入视频播放页面后,有三块区域,最上方的XComponent,中间的画中画控制按钮以及下方的回调信息显示框
  • 点击开启后,应用手动拉起画中画,视频在画中画播放,返回桌面视频依旧画中画播放;点击关闭后,画中画播放的视频返回XComponent播放,同时返回桌面不会拉起画中画。
  • 点击自动拉起画中画后,返回桌面时应用自动拉起画中画,视频画中画播放。
  • 在播放页面进行画中画播放时,XComponent框会提示当前视频正在以画中画播放
  • 回调信息显示框会显示当前状态错误原因以及按钮事件和状态,参考:[VideoPlay.ets]。
/** Copyright (c) 2024 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import { PiPWindow } from '@kit.ArkUI';
import { JSON } from '@kit.ArkTS';
import { Constants } from '../constants/Constants';
import { AVPlayer } from './AVPlayer';
import Logger from '../utils/Logger';const TAG = Constants.NAV_DESTINATION_NAME;@Extend(Text)
function textType() {.padding({ left: $r('app.integer.other_padding') }).fontWeight(FontWeight.Bold).fontSize($r('app.integer.text_size')).alignSelf(ItemAlign.Start)
}@Extend(Text)
function msgType() {.padding({ left: $r('app.integer.other_padding') }).fontSize($r('app.integer.text_size')).fontColor($r('app.color.Message_color')).alignSelf(ItemAlign.Start)
}@Component
export struct PlayVideo {@Consume('pageInfos') pageInfos: NavPathStack;@State curState: string = '';@State curError: string = '';@State buttonAction: string = '';@State isAutoPull: boolean = false;@State isLightBackground: boolean = false;@State hintMsgVisibility: boolean = false;@State pipTypeString: string = '';mXComponentController = new XComponentController();surfaceId = '';navigationId: string = '';player?: AVPlayer;pipController?: PiPWindow.PiPController;eventHub = getContext().eventHub;private scrollerForScroll: Scroller = new Scroller()aboutToAppear(): void {this.eventHub.on('onStateChange', (fg: boolean) => {if (fg && this.curState === 'STARTED') {this.stopPip();}});}async startPip() {if (!this.pipController) {await this.createPipController();}if (!this.pipController) {Logger.info(`[${TAG}] pipController create error`);return;}await this.pipController.startPiP();}async stopPip() {if (!this.pipController) {Logger.info(`[${TAG}] pipController is not exist`);return;}await this.pipController.stopPiP();}async createPipController() {this.pipController = await PiPWindow.create({context: getContext(this),componentController: this.mXComponentController,navigationId: this.navigationId,templateType: PiPWindow.PiPTemplateType.VIDEO_PLAY});this.pipController.on('stateChange', (state: PiPWindow.PiPState, reason: string) => {this.onStateChange(state, reason);});this.pipController.on('controlPanelActionEvent', (event: PiPWindow.PiPActionEventType, status?: number) => {this.onActionEvent(event, status);});}destroyPipController() {if (!this.pipController) {return;}this.pipController.off('stateChange');this.pipController.off('controlPanelActionEvent');this.pipController = undefined;}onStateChange(state: PiPWindow.PiPState, reason: string) {switch (state) {case PiPWindow.PiPState.ABOUT_TO_START:this.curState = 'ABOUT_TO_START';this.curError = Constants.ERROR_BY_DEFAULT;break;case PiPWindow.PiPState.STARTED:this.curState = 'STARTED';this.curError = Constants.ERROR_BY_DEFAULT;break;case PiPWindow.PiPState.ABOUT_TO_STOP:this.curState = 'ABOUT_TO_STOP';this.curError = Constants.ERROR_BY_DEFAULT;break;case PiPWindow.PiPState.STOPPED:this.player?.updatePlayStatus(true);this.player?.play();this.curState = 'STOPPED';this.curError = Constants.ERROR_BY_DEFAULT;break;case PiPWindow.PiPState.ABOUT_TO_RESTORE:this.curState = 'ABOUT_TO_RESTORE';this.curError = Constants.ERROR_BY_DEFAULT;break;case PiPWindow.PiPState.ERROR:this.curState = 'ERROR';this.curError = reason;break;default:break;}Logger.info(`[${TAG}] onStateChange: ${this.curState}, reason: ${reason}`);}onActionEvent(event: PiPWindow.PiPActionEventType, status: number | undefined) {switch (event) {case 'playbackStateChanged':if (status === 0) {this.player?.updatePlayStatus(false);this.player?.pause();} else {this.player?.updatePlayStatus(true);this.player?.play();}break;default:break;}this.buttonAction = event + `-status:${status}`;Logger.info(`[${TAG}] onActionEvent: ${this.buttonAction} status:${status}}`);}build() {Stack() {NavDestination() {Column({ space: Constants.SPACE }) {Stack() {Text($r('app.string.current_video_pip_play')).fontColor($r('app.color.XComponent_text_color')).margin({ bottom: $r('app.integer.x_component_marg_bottom') }).visibility(this.hintMsgVisibility ? Visibility.Visible : Visibility.Hidden)XComponent({ id: 'video', type: 'surface', controller: this.mXComponentController }).onLoad(() => {Logger.info(`[${TAG}] XComponent onLoad`);this.surfaceId = this.mXComponentController.getXComponentSurfaceId();this.player = new AVPlayer(this.surfaceId, Constants.AVPLAYER_TYPE);this.player.avPlayerFdSrc();}).onDestroy(() => {this.player?.stopAvPlayer();Logger.info(`[${TAG}] XComponent onDestroy`);}).size({ width: Constants.X_COMPONENT_WIDTH, height: $r('app.float.x_component_height') }).margin({ top: $r('app.integer.x_component_marg_top') }).backgroundColor(Color.Transparent).align(Alignment.Bottom).id('x_component')}.size({ width: Constants.X_COMPONENT_WIDTH, height: $r('app.float.x_component_height') }).alignContent(Alignment.Bottom).backgroundColor($r('app.color.XComponent_backgroundColor'))Scroll(this.scrollerForScroll) {Column({ space: Constants.SPACE }) {this.ControlPip()this.AutoPip()this.CallbackMessage()}.width(Constants.NAV_DESTINATION_WIDTH)}.layoutWeight(Constants.SCROLL_LAY_OUT_WEIGHT).scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off).edgeEffect(EdgeEffect.Spring)}.width(Constants.NAV_DESTINATION_WIDTH).height(Constants.NAV_DESTINATION_HEIGHT)}.hideTitleBar(true).backgroundColor($r('app.color.Play_backgroundColor')).onBackPressed(() => {// Eject the top-of-the-stack element of the routing stack.const popDestinationInfo = this.pageInfos.pop();Logger.info('pop' + 'return value' + JSON.stringify(popDestinationInfo));return true;})}}@BuilderControlPip() {Row({ space: Constants.SPACE }) {Button($r('app.string.start')).width($r('app.integer.control_button_width')).onClick(() => {this.startPip();this.hintMsgVisibility = true;})Button($r('app.string.stop')).width($r('app.integer.control_button_width')).onClick(() => {this.stopPip();this.hintMsgVisibility = false;})}.size({ width: Constants.CONTROL_WIDTH, height: $r('app.integer.control_height') }).justifyContent(FlexAlign.SpaceAround).id('pip_control')}@BuilderAutoPip() {Row() {Text($r('app.string.auto')).width($r('app.integer.auto_text_width')).fontSize($r('app.integer.text_size')).fontWeight(FontWeight.Bold).padding({ left: $r('app.integer.other_padding') })Toggle({ type: ToggleType.Switch, isOn: this.isAutoPull }).width($r('app.integer.auto_button_width')).height($r('app.integer.auto_button_height')).selectedColor($r('app.color.Toggle_selectedColor')).padding({ right: $r('app.float.toggle_padding') }).onChange(async (isOn: boolean) => {this.isAutoPull = isOn;if (!this.pipController) {await this.createPipController();}this.pipController?.setAutoStartEnabled(this.isAutoPull);this.hintMsgVisibility = true;})}.width(Constants.AUTO_PIP_WIDTH).height($r('app.integer.auto_pip_height')).borderRadius($r('app.integer.auto_button_board_radius')).justifyContent(FlexAlign.SpaceBetween).backgroundColor($r('app.color.start_window_background'))}@BuilderCallbackMessage() {Column({ space: Constants.SPACE }) {Text($r('app.string.callback_message')).fontColor($r('app.color.Text_color')).padding({ right: $r('app.integer.callback_text_padding') })Column() {Text($r('app.string.current_status')).textType()Text(this.curState).msgType()}.size({width: Constants.CONTROL_WIDTH,height: $r('app.integer.control_height')}).backgroundColor($r('app.color.Callback_message_backgroundColor')).borderRadius($r('app.integer.auto_button_board_radius')).justifyContent(FlexAlign.SpaceAround).id('current_state')Column() {Text($r('app.string.current_error')).textType()Text(this.curError).msgType()}.size({width: Constants.CONTROL_WIDTH,height: $r('app.integer.control_height')}).backgroundColor($r('app.color.Callback_message_backgroundColor')).borderRadius($r('app.integer.auto_button_board_radius')).justifyContent(FlexAlign.SpaceAround).id('current_error')Column() {Text($r('app.string.current_action')).textType()Text(this.buttonAction).msgType()}.size({width: Constants.CONTROL_WIDTH,height: $r('app.integer.control_height')}).backgroundColor($r('app.color.Callback_message_backgroundColor')).borderRadius($r('app.integer.auto_button_board_radius')).justifyContent(FlexAlign.SpaceAround).id('current_action')}}
}

以上就是本篇文章所带来的鸿蒙开发>鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

在这里插入图片描述

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!


http://www.ppmy.cn/server/93242.html

相关文章

研究人员可以采用什么策略来批判性地评估和综合其领域的不同文献

VersaBot Literature Review 一键生成文献综述 研究人员可以采用各种策略来批判性地评估和综合其领域内的不同文献; 评估策略 审查方法论: 分析每个来源中使用的研究设计、样本选择、数据收集和分析方法。考虑每种方法的潜在偏见、局限性和优势。评估…

活动报名小程序

#活动报名工具# # 活动报名小程序 ## 项目简介 一款通用的活动报名工具,包含活动展示,微信支付,订单管理,分享评价等功能。 品客聚精彩,有你才精彩!不只有线下活动还可以进行线上裂变活动。 …

fastapi之零

FastAPI 详细介绍 FastAPI 是一个现代、快速(高性能)的 web 框架,用于构建 API。它基于标准的 Python 类型提示,使用 Starlette 作为 web 框架,Pydantic 进行数据验证和解析。以下是对 FastAPI 的详细介绍&#xff0c…

FFmpeg研究

1.FFmpeg介绍 FFmpeg的全称是“Fast Forward Moving Picture Expert Group”,组件由命令行应用程序和函数库两部分组成。通俗概括来说,FFmpeg 是一个免费的开源程序库,一个多媒体音视频处理分析工具软件,且提供命令行方式调用&am…

信息安全工程师下午题

试题三(共 18 分) 阅读下列说明和图,回答问题 1 至问题 9,将解答填入答题纸的对应栏内。【说明】Windows 系统日志是记录系统中硬件、软件和系统问题的信息,同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因&#xff0c…

14 B端产品的运营管理

通过运营找到需求并通过交换价值提供供给,再逐步扩大规模、站稳脚跟,辅助产品在商业竞争中获胜。 B端产品运营框架 1. 打通渠道 目的:触达客户。 环节:文案策划、活动策划→广告渠道推广→线下BD。 线下BD:通过见面…

JAVA(IO流-字节流)day 7.29

ok家人们今天继续学习IO流, 一.字节流 存储时,都是使用二进制来保存。 2.1 字节输出流OutputStream OutputStream是字节输出流的超类(父类), 方法 public abstract void write(int b): 一次写一个字节数据。pub…

PHP框架详解之Symfony框架

一、框架概述 起源与开发者:Symfony由SensioLabs(现为Symfony公司)开发,最初由Fabien Potencier于2005年创建。功能定位:Symfony通过提供一套可重用的组件和标准化的框架,帮助开发者快速构建Web应用、API、…