HarmonyOS鸿蒙学习笔记(15)Swiper实现抖音切换视频播放效果

news2025/2/26 22:00:01

Swiper实战

  • 1、项目结构
  • 2、PageVideo和PlayView简单说明
    • 2.1 @State变量的使用
    • 2.2 @Link和@Watch变量的使用
    • 2.3、Swiper的使用和PlayView的初始化
    • 2.4、页面可见状态发生改变时对视频进行暂停和播放
    • 2.5 PlayView和PageVidew源码:
  • 参考资料:

1、项目结构

前面写了Swiper控件的初步使用,本文结合@Link ,@State、@Watch来实现类似抖音滑动播放视频的效果。本文源码地址Swiper实战。
项目结构如下图:
在这里插入图片描述
其中PageVideo.ets是APP首页,用来提供视频播放的列表。PlayView.ets是视频播放器,随着PageVideo.ets的滚动切换视频,来通知PlayView.ets暂停和开始视频的播放。VideoItem.ets包含着视频信息,比如视频播放地址等。

2、PageVideo和PlayView简单说明

2.1 @State变量的使用

PageVideo提供了两个@State修饰的变量:pageShownumber,这两个状态发生改变的时候会通知PlayView对播放器进行调整。

@Entry
@Component
export struct PageVideo {
  @State videoArray: Array<VideoItem> = initializeOnStartup() // 数据源

  //当切换视频的时候,会通知PlayView播放当前的视频,同时关闭上一个视频
  @State index: number = 0 // 当前滑动的索引位置
  @State pageShow: boolean = false // 当前页面是否可见,仅对@Entry修饰的主页面PageVideo而言,子组件需要用@Watch监听该状态
}  

2.2 @Link和@Watch变量的使用

那么PlayView是如何感知这两个状态发生了改变了呢?此时@Link标签就起到了作用.PlayView代码如下:

@Component
export struct PlayView {
  // 四条控制页面可见、页面不可见的控制
  private isShow: boolean = false // 是否是可见状态
  @Link @Watch("needPageShow") index: number // 监听父组件索引index状态变化, 不能在组件内部进行初始化
  @Link @Watch("needPageShow") pageShow: boolean // 监听父组件是否可见pageShow状态变化, 不能在组件内部进行初始化
 }

其中也是用了@Watch标签定义了needPageShow的方法,当number和pageShow的值发生变化的时候会调用该方法,用来控制视频暂停上一个和播放当前的视频:


  // 监听父组件index、pageShow属性变化就会触发的方法,@Watch
  needPageShow() {
    console.log("playView*** needPageShow")
    if (this.pageShow) { // 页面可见时触发
      if (this.position == this.index) { // 判断index与当前所在位置是否相同
        this.isShow = true;
        this.onPageShow()
      } else {
        if (this.isShow) { // 已经是可见的状态改为不可见,并触发不可见方法回调
          this.isShow = false;
          this.onPageSwiperHide()
        }
      }
    } else { // 页面不可见触发
      if (this.position == this.index) {
        if (this.isShow) { // 已经是可见的状态改为不可见,并触发不可见方法回调
          this.isShow = false;
          this.onPageHide()
        }
      }
    }
  }

2.3、Swiper的使用和PlayView的初始化

在PlayVideo中根据视频的个数初始化了Swiper控件,该控件的子控件就是PlayView,注意在初始化PlayView时,初始化@Link修饰的变量时需要用$修饰。

 build() {
    Column() {
      Swiper() {
        ForEach(this.videoArray.map((item, index) => {
          return { i: index, data: item };
        }),
          item => {
          //初始化@Link修饰的变量时需要用$修饰。
            PlayView({ index: $index, pageShow: $pageShow, item: item.data, position: item.i })
          },
          item => item.data.id.toString())
      }
      .indicator(false) // 默认开启指示点
      .loop(false) // 默认开启循环播放
      .vertical(true) // 默认横向切换、更改为竖向滑动
      .onChange((index: number) => {//滚动Swiper的时候用来监听页面的变化
        //此处会触发PlayView的needPageShow方法。
        this.index = index
      })
    }
  }

2.4、页面可见状态发生改变时对视频进行暂停和播放

我们按home键的时候,APP进入后台。也就是在APP前后台状态发生改变的时候,需要通知PlayView进行暂停或者播放。在PlayVideo里面实现了onPageShowonPageHide,在这两个方法里面改变pageShow的值,当pageShow的值发生改变时,PlayView因为@Link的作用会监听到值的改变,从而执行PlayViewneedPageShow方法用来控制视频的暂停和播放。

 // 当此页面可见时触发,仅@Entry修饰的自定义组件生效
  onPageShow(): void  {
    //当pageShow的值发生改变时,PlayView因为@Link的作用会监听到值的改变,从而
    this.pageShow = true;
  }

  // 当此页面不可见时触发,仅@Entry修饰的自定义组件生效
  onPageHide(): void  {
    this.pageShow = false;
  }

2.5 PlayView和PageVidew源码:

二者全部的源码如下,当然读者可以通过Swiper实战下载整个项目来分析和学习。


import {VideoItem} from '../play/VideoItem'

@Component
export struct PlayView {
  // 四条控制页面可见、页面不可见的控制
  private isShow: boolean = false // 是否是可见状态
  @Link @Watch("needPageShow") index: number // 监听父组件索引index状态变化, 不能在组件内部进行初始化
  @Link @Watch("needPageShow") pageShow: boolean // 监听父组件是否可见pageShow状态变化, 不能在组件内部进行初始化
  private position: number // 当前页面所在位置

  @ObjectLink private item: VideoItem // @Observed与@ObjectLink配合使用实现 class修饰的模型数据改变触发UI更新
  @State private playState: number = 0 // 0表示停止播放--stop   1表示开始播放--start   2表示暂停播放--pause

  // @ts-ignore
  private videoController: VideoController = new VideoController() // 视频播放器控制器

  build() {
    Stack({ alignContent: Alignment.Center | Alignment.End }) {
      Video({
        src: this.item.src, // 视频播放地址
        controller: this.videoController // 视频播放器控制器
      })
        .controls(false) // 不需要控制栏
        .autoPlay(this.playState == 1 ? true : false) // 首次可见状态自动播放
        .objectFit(ImageFit.Contain) // 视频窗口自适应视频大小
        .loop(true) // 循环播放
        .onClick(() => { // 点击播放、再点击暂停播放
          if (this.playState == 1) {
            this.playState = 2;
            this.videoController.pause();
          } else if (this.playState == 2) {
            this.playState = 1;
            this.videoController.start();
          }
        })

      Column() {
        Image(this.item.isLikes ? $r('app.media.vote1') : $r('app.media.vote0')) // 点赞图标
          .width(36).height(36)
          .onClick(() => {
            if (this.item.isLikes) {
              this.item.likesCount--;
            } else {
              this.item.likesCount++;
            }
            this.item.isLikes = !this.item.isLikes;
          }).margin({ top: 40 })
        Text(this.item.likesCount == 0 ? '点赞' : ('' + this.item.likesCount)).fontSize(16).fontColor(0xffffff) // 评论图标

        Image($r('app.media.comment'))
          .width(36).height(36).margin({ top: 20 })
        Text(this.item.commentCount == 0 ? '评论' : ('' + this.item.commentCount))
          .fontSize(16)
          .fontColor(0xffffff) // 转发图标

        Image($r('app.media.share'))
          .width(36).height(36).margin({ top: 20 })
      }.offset({ x: '-5%', y: '-10%' }) // 位置调整

      Text(this.item.title)
        .fontSize(16)
        .fontColor(0xffffff)
        .margin(10)
        .offset({ x: '-50%', y: '40%' })
    }.backgroundColor(Color.Black)
    .width('100%')
    .height('100%')
  }

  // 自定义的方法。页面可见状态会被调用,多次调用
  onPageShow(): void  {
    console.log("pageView*** OnPageShow")
    if (this.playState != 1) {
      this.playState = 1;
      this.videoController.start();
    }
  }

  // 自定义的方法。页面不可见状态会被调用,多次调用,这种不可见是Swiper滑动时触发的
  private onPageSwiperHide(): void  {
    console.log("playView*** onPageSwiperHide")
    if (this.playState != 0) {
      this.playState = 0;
      this.videoController.stop(); // 停止视频播放
    }
  }

  // 自定义的方法。页面不可见状态会被调用,多次调用,这种不可见是点击页面跳转、或者应用回到桌面时触发的
  onPageHide(): void  {
    console.log("playView*** onPageHide")
    if (this.playState != 2) {
      this.playState = 2;
      this.videoController.pause(); // 暂停视频播放
    }
  }

  // 监听父组件index、pageShow属性变化就会触发的方法,@Watch
  needPageShow() {
    console.log("playView*** needPageShow")
    if (this.pageShow) { // 页面可见时触发
      if (this.position == this.index) { // 判断index与当前所在位置是否相同
        this.isShow = true;
        this.onPageShow()
      } else {
        if (this.isShow) { // 已经是可见的状态改为不可见,并触发不可见方法回调
          this.isShow = false;
          this.onPageSwiperHide()
        }
      }
    } else { // 页面不可见触发
      if (this.position == this.index) {
        if (this.isShow) { // 已经是可见的状态改为不可见,并触发不可见方法回调
          this.isShow = false;
          this.onPageHide()
        }
      }
    }
  }
}


import { VideoItem, initializeOnStartup } from '../play/VideoItem'
import { PlayView } from '../play/PlayView'

@Entry
@Component
export struct PageVideo {
  @State videoArray: Array<VideoItem> = initializeOnStartup() // 数据源

  //当切换视频的时候,会通知PlayView播放当前的视频,同时关闭上一个视频
  @State index: number = 0 // 当前滑动的索引位置
  @State pageShow: boolean = false // 当前页面是否可见,仅对@Entry修饰的主页面PageVideo而言,子组件需要用@Watch监听该状态

  build() {
    Column() {
      Swiper() {
        ForEach(this.videoArray.map((item, index) => {
          return { i: index, data: item };
        }),
          item => {
            PlayView({ index: $index, pageShow: $pageShow, item: item.data, position: item.i })
          },
          item => item.data.id.toString())
      }
      .indicator(false) // 默认开启指示点
      .loop(false) // 默认开启循环播放
      .vertical(true) // 默认横向切换、更改为竖向滑动
      .onChange((index: number) => {
        this.index = index
      })
    }
  }

  // 当此页面可见时触发,仅@Entry修饰的自定义组件生效
  onPageShow(): void  {
    this.pageShow = true;
  }

  // 当此页面不可见时触发,仅@Entry修饰的自定义组件生效
  onPageHide(): void  {
    this.pageShow = false;
  }
}

参考资料:

鸿蒙实战项目表,分享知识与见解,一起探索HarmonyOS的独特魅力。https://gitee.com/harmonyos/codelabs

HarmonyOS鸿蒙学习笔记(12)@Link的作用
HarmonyOS鸿蒙学习笔记(8)Swiper实现轮播滚动效果
HarmonyOS鸿蒙学习笔记(5)@State作用说明和简单案例
Swiper实战
@Watch标签的作用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/9853.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

22服务-ReadDataByIdentifier

诊断协议那些事儿 诊断协议那些事儿专栏系列文章&#xff0c;本文介绍数据传输服务下的22服务ReadDataByIdentifier&#xff0c;允许客户端通过一个或多个dataldentifier向标识的服务器请求dataRecord&#xff08;数据记录值&#xff09;。 文章目录诊断协议那些事儿一、22服…

技术宅星云的Mac系统使用经验分享

技术宅星云的Mac系统使用经验分享系统维护1.1 Mac OSX 卡顿严重1.2 开启/禁止.DS_store文件生成1.3 显示/隐藏 系统文件夹系统维护 这篇博文分享使用Mac 系统中的一些优化经验。 1.1 Mac OSX 卡顿严重 今天不知道怎么回事&#xff0c;系统突然卡得不要不要的&#xff0c;各种…

Airtest新手升级:一个相对完整的纯.py脚本是怎样子的

1. 前言 一直以来&#xff0c;Airtest的教程都倾向于编写 .air 脚本&#xff0c;但本质上&#xff0c;它还是python脚本来的。今天我们就来补上这个缺口&#xff0c;一起来看下一个相对完整的纯 .py 脚本是什么样子的。 2. 例子一&#xff1a;纯py的Airtest脚本 有时候&…

Python-新建-Django项目-调试-显示mysql数据库表内容-HelloWorld

文章目录1.Pycharm-开发编辑器2.HelloWorld程序范例3.代码调试4.连接数据库-mysql4.1.安装好mysql数据库4.2.创建项目4.3.数据库表转模型4.4.前端展示5.总结1.Pycharm-开发编辑器 文件->新建项目->选择Django。接着在控制台输入命令&#xff1a; python -m django --ver…

【C语言】分支语句 循环语句 _训练题型加深理解

1.分支语句 自从学习编程以来每天都在写分支语句&#xff0c;那么什么是分支语句呢&#xff1f; 下面举两个生动的例子来更好的理解分支语句&#xff1a; 比如我们买东西&#xff0c;要么支付现金&#xff0c;要么使用微信或者支付宝。在大学如果你好好学习&#xff0c;校招…

SpringBoot简单使用MongoDB

SpringBoot简单使用MongoDB一、配置步骤1、application.yml2、pom3、entity4、mapper二、案例代码使用1、库前期准备上一篇安装MongoDB地址http://t.csdn.cn/G4oYJ 一、配置步骤 进入mongodb中创建数据库和用户 # &#xff08;1&#xff09;授权 # 我的管理员是root&#xf…

umi项目本地开发环境远程打开的问题

qiankun主应用加载子应用时&#xff0c;url指定了localhost const getEntry (base: string, port: number) > {const host: string location.hostnamereturn process.env.NODE_ENV development? http://${host}:${port}${base}: ${base}/index.html }而getEntry是用于q…

Cloud Keys Delphi Edition安全地存储

Cloud Keys Delphi Edition安全地存储 使用流行的基于云的密钥管理服务安全地管理密钥和机密。 云密钥可以轻松地将基于云的密钥和秘密管理与任何支持的平台或开发技术集成。这些易于使用的组件可用于与流行的云密钥管理提供商(如Amazon KMS、Amazon AWS Secrets、Azure key Va…

初识 Node.js 与内置模块:初识 Node.js及Node.js 环境的安装

回顾与思考 1. 已经掌握了哪些技术 2. 浏览器中的 JavaScript 的组成部分 3. 思考&#xff1a;为什么 JavaScript 可以在浏览器中被执行 4. 思考&#xff1a;为什么 JavaScript 可以操作 DOM 和 BOM 每个浏览器都内置了 DOM、BOM 这样的 API 函数&#xff0c;因此&#xff0c;…

【云原生之k8s】k8s资源限制以及探针检查

文章目录一、资源限制1、资源限制的使用2、reuqest资源&#xff08;请求&#xff09;和limit资源&#xff08;约束&#xff09;3、Pod和容器的资源请求和限制4、官方文档示例5、资源限制实操5.1 编写yaml资源配置清单5.2 释放内存&#xff08;node节点&#xff0c;以node01为例…

Moonbeam Illuminate/22线上生态盛会|Derek开场演讲

TL;DR Derek&#xff1a;Moonbeam是我认为最佳的实现Web3梦想的平台。一年中近300个项目已经部署在了Moonbeam生态&#xff0c;发展显著优于行业平均。Moonbeam正在构建被成为“Connected Contracts”的原生跨链方案。Moonbeam基金会新设立Moonbeam加速器&#xff0c;帮助Moon…

时间序列预测之为何舍弃LSTM而选择Informer?(Informer模型解读)

LSTM的劣势 Figure 1: (a) LSTF can cover an extended period than the short sequence predictions, making vital distinction in policy-planning and investment-protecting. (b) The prediction capacity of existing methods limits LSTF’s performance. E.g., startin…

Nginx快速入门及配置文件结构

Nginx快速入门教程Nginx 简介Nginx 特性Nginx 架构Nginx 相比Apache的优点Nginx 的安装启动、停止和重新加载 Nginx 配置Nginx 配置文件结构Nginx 工作流程总结后言Nginx 简介 Nginx是 HTTP 和反向代理服务器&#xff0c;邮件代理服务器&#xff0c;以及 Igor Sysoev 最初编写…

传统防火墙与Web应用程序防火墙(WAF)的区别

前言 由于WEB应用防火墙&#xff08;WAF&#xff09;的名字中有“防火墙”三个字&#xff0c;因此很多人都会将它与传统防火墙混淆。实际上&#xff0c;二者之间的有着很大的差别。传统防火墙专注在网络层面&#xff0c;提供IP、端口防护。而WAF是专门为保护基于Web的应用程序…

学生用白炽灯好还是led灯好?2022最专业学生护眼灯推荐

现阶段的学生视力都普遍出现近视低龄化&#xff0c;所以在护眼方面&#xff0c;家长都非常重视的&#xff0c;有人问&#xff1a;学生用白炽灯好还是led灯好&#xff1f; 我的回答是LED灯更适合现在家庭使用&#xff0c;给大家分析一下。 白炽灯是由灯丝发热产生光亮&#xff…

多层串联拼接网络

🍿*★,*:.☆欢迎您/$:*.★* 🍿 目录 背景 正文 总结 背景描述

Pytorch ——特征图的可视化

文章目录前言一、torchvision.models._utils.IntermediateLayerGetter*注意&#xff1a;torcvision的最新版本0.13&#xff0c;已经取消了pretrainedTrue这个参数&#xff0c;并且打算在0.15版正式移除&#xff0c;如果用pretrained这个参数会出现warring警告。现在加载与训练权…

【项目实战】springboot+vue舞蹈课程在线学习系统-java舞蹈课程学习打卡系统的设计与实现

注意&#xff1a;该项目只展示部分功能&#xff0c;如需了解&#xff0c;评论区咨询即可。 本文目录1.开发环境2 系统设计2.1 背景意义2.2 技术路线2.3 主要研究内容3 系统页面展示3.1 学生3.2 教师页面3.3 管理员页面4 更多推荐5 部分功能代码5.1 查看学生打卡5.2 文件上传下载…

天翼云实时云渲染,助力打造世界VR产业大会云上之城

2022年11月12日&#xff0c;2022世界VR产业大会于江西南昌开幕。11月13日&#xff0c;以“共建元宇宙生态&#xff0c;点亮新数智未来”为主题的中国电信生态论坛召开。由天翼云携手新国脉数字文化股份有限公司&#xff08;简称“国脉文化”&#xff09;打造的元宇宙家园国脉大…

【力扣刷题】只出现一次的数字

&#x1f517; 题目链接 题目描述 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 …