1. Project structure
The initial use of the Swiper control was written earlier . This article combines @Link , @State , @Watch to achieve an effect similar to vibrato sliding video playback. The source code address of this article is Swiper actual combat .
The project structure is as shown in the figure below:
it PageVideo.ets
is the home page of the APP, which is used to provide a list of video playback. PlayView.ets
It is a video player that PageVideo.ets
switches videos with scrolling to notify PlayView.ets
pausing and starting video playback. VideoItem.ets contains video information, such as video playback address and so on.
2. Brief description of PageVideo and PlayView
2.1 Use of @State variables
PageVideo
@State
Two modified variables are provided : pageShow
and number
, when these two states change, PlayView
the player will be notified to adjust.
@Entry
@Component
export struct PageVideo {
@State videoArray: Array<VideoItem> = initializeOnStartup() // 数据源
//当切换视频的时候,会通知PlayView播放当前的视频,同时关闭上一个视频
@State index: number = 0 // 当前滑动的索引位置
@State pageShow: boolean = false // 当前页面是否可见,仅对@Entry修饰的主页面PageVideo而言,子组件需要用@Watch监听该状态
}
2.2 Use of @Link and @Watch variables
So PlayView
how do you perceive that these two states have changed? At this point, @Link
the label plays a role. PlayView
The code is as follows:
@Component
export struct PlayView {
// 四条控制页面可见、页面不可见的控制
private isShow: boolean = false // 是否是可见状态
@Link @Watch("needPageShow") index: number // 监听父组件索引index状态变化, 不能在组件内部进行初始化
@Link @Watch("needPageShow") pageShow: boolean // 监听父组件是否可见pageShow状态变化, 不能在组件内部进行初始化
}
Among them, the method defined by the @Watch tag is also used needPageShow
. When the value of number and pageShow changes, this method will be called to control the video to pause the previous one and play the current video:
// 监听父组件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, the use of Swiper and the initialization of PlayView
In PlayVideo, Swiper
the control is initialized according to the number of videos, and the sub-control of the control is PlayView
. Note that when initializing PlayView, it needs to be modified with $ when initializing the @Link modified variable.
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. Pause and play the video when the visible state of the page changes
When we press the home button, the APP enters the background. That is, when the state of the front and back of the APP changes, it needs to be notified PlayView
to pause or play. Implemented onPageShow
and in PlayVideo, the value onPageHide
changed in these two methods , when the value of the value changes, because the function will monitor the change of the value, and the executed method is used to control the pause and playback of the video.pageShow
pageShow
PlayView
@Link
PlayView
needPageShow
// 当此页面可见时触发,仅@Entry修饰的自定义组件生效
onPageShow(): void {
//当pageShow的值发生改变时,PlayView因为@Link的作用会监听到值的改变,从而
this.pageShow = true;
}
// 当此页面不可见时触发,仅@Entry修饰的自定义组件生效
onPageHide(): void {
this.pageShow = false;
}
2.5 PlayView and PageVidew source code:
All the source codes of the two are as follows. Of course, readers can download the entire project through Swiper to analyze and learn.
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;
}
}
References:
HarmonyOS practical project list , share knowledge and insights, and explore the unique charm of HarmonyOS together. https://gitee.com/harmonyos/codelabs
HarmonyOS Hongmeng Study Notes (12) The role of @Link HarmonyOS Hongmeng Study Notes (8) Swiper realizes the effect
of carousel scrolling