自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

OpenHarmony仿視頻播放器應(yīng)用-愛(ài)電影(三)

系統(tǒng) OpenHarmony
本篇我們?cè)敿?xì)的說(shuō)說(shuō)電影播放頁(yè)面開(kāi)發(fā)涉及的內(nèi)容,首先我們來(lái)看下電影播放頁(yè)面的設(shè)計(jì)圖。

??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??

??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??

??https://ost.51cto.com??

效果

??在線視頻??

接??上一篇??,主頁(yè)上顯示了電影資源,點(diǎn)擊你想看的電影會(huì)跳轉(zhuǎn)至電影播放頁(yè)面,接下來(lái)我們?cè)敿?xì)的說(shuō)說(shuō)電影播放頁(yè)面開(kāi)發(fā)涉及的內(nèi)容,首先我們來(lái)看下電影播放頁(yè)面的設(shè)計(jì)圖,如下:

#創(chuàng)作者激勵(lì)#OpenHarmony仿視頻播放器應(yīng)用-愛(ài)電影(三)-開(kāi)源基礎(chǔ)軟件社區(qū)

#創(chuàng)作者激勵(lì)#OpenHarmony仿視頻播放器應(yīng)用-愛(ài)電影(三)-開(kāi)源基礎(chǔ)軟件社區(qū)

從上圖我們知道,從結(jié)構(gòu)上來(lái)講可以分為上下兩部分組成,上部分是視頻播放器,下部分是電影簡(jiǎn)介。
視頻播放器:由前后兩層,底層是視頻播放,頂層是視頻播放控制器,包括了返回按鍵、顯示視頻名稱、控制視頻的播放、暫停、更新進(jìn)度、全屏顯示、視頻總時(shí)長(zhǎng)和當(dāng)前播放視頻時(shí)間點(diǎn)。
電影簡(jiǎn)介:包括電影的介紹以及一些推薦的電影,點(diǎn)擊“簡(jiǎn)介”彈窗顯示該電影的詳細(xì)信息,包括:電影的類型、來(lái)源、評(píng)分、熱度、演員、詳細(xì)的劇情等。

項(xiàng)目開(kāi)發(fā)

開(kāi)發(fā)環(huán)境

硬件平臺(tái):DAYU2000 RK3568
系統(tǒng)版本:OpenHarmony 3.2 beta5
SDK:9(3.2.10.6)
IDE:DevEco Studio 3.1 Beta1 Build Version: 3.1.0.200, built on February 13, 2023

程序代碼

1、Playback.ets

首先我們看下視頻播放頁(yè)面的代碼:

import { VideoView } from '../view/VideoView';
import { PLAYBACK_SPEED, PLAYBACK_STATE } from '../model/Playback'
import router from '@ohos.router';
import { VideoListView } from '../view/VideoListView'
import { VideoData } from '../model/VideoData'
import { MockVideoData } from '../model/MockVideoData'
import { VideoDataUtils } from '../utils/VideoDataUtils'
import { VideoIntroduceView } from '../view/VideoIntroduceView'
import { VideoSpeed } from '../model/VideoSpeed'
import emitter from '@ohos.events.emitter';
import { CommonData } from '../model/CommonData'
/**
* 視頻播放頁(yè)面
*/
const TAG: string = 'Playback'
@Entry
@Component
struct Playback {
@State mTag: string = TAG
private name: string
private introduction: string
@State uri: any = null
@State previewImage: any = null
private actors: string | Resource
private directs: string | Resource
@State rateIndex: number = 1
@State rate: VideoSpeed = PLAYBACK_SPEED[1]
@State @Watch('scrollChange') scrollIndex: number = 0
@State likeVideoList: Array<VideoData> = []
private scrollerForScroll: Scroller = new Scroller()
@State isShowIntroduce: boolean = false // 是否顯示簡(jiǎn)介
@State mVideoData: VideoData = null
@State isScrollClose: boolean = false
@Provide('play_time') curTime: number = 0
@State videoState: string = PLAYBACK_STATE.INIT
@Provide('show_operation') isShowOperation: boolean = true
aboutToAppear() {
console.info(`${TAG} aboutToAppear curTime:${this.curTime}`)
this.initData()
}
initData() {
this.likeVideoList = MockVideoData.getVideoList()
// 獲取當(dāng)前需要播放的電影資源信息
this.mVideoData = router.getParams()['video_data']
this.name = this.mVideoData.name
this.uri = this.mVideoData.uri
this.previewImage = this.mVideoData.image
this.actors = VideoDataUtils.getUser(this.mVideoData.actors)
this.directs = VideoDataUtils.getUser(this.mVideoData.directs)
this.introduction = this.mVideoData.introduction
}
onCloseIntroduce() {
this.isShowIntroduce = false
}
onScreen(isFull: boolean) {
console.info(`${TAG} onScreen ${isFull} mVideoData:${JSON.stringify(this.mVideoData)} curTime:${this.curTime} videoState:${this.videoState}`)
if (isFull) {
router.pushUrl({
url: 'pages/FullScreen',
params: {
video_data: this.mVideoData,
cur_time: this.curTime, // 當(dāng)前播放時(shí)間
video_state: this.videoState // 播放狀態(tài)
}
})
}
}
scrollChange() {
if (this.scrollIndex === 0) {
this.scrollToAnimation(0, 0)
} else if (this.scrollIndex === 2) {
this.scrollToAnimation(0, 280)
}
}
onPageShow() {
// 豎屏顯示
emitter.emit({
eventId: CommonData.EVENT_WINDOW_PORTRAIT_ID
})
}
onPageHide() {
console.info(`${TAG} onPageHide`)
}
scrollToAnimation(xOffset, yOffset) {
this.scrollerForScroll.scrollTo({
xOffset: xOffset,
yOffset: yOffset,
animation: {
duration: 3000,
curve: Curve.FastOutSlowIn
}
})
}
build() {
Stack() {
Column() {
Stack({
alignContent: Alignment.TopStart
}) {
VideoView({
_TAG: this.mTag,
videoUri: $uri,
previewUri: $previewImage,
videoRate: $rate,
videoRateIndex: $rateIndex,
onScreen: this.onScreen.bind(this),
isFullScreen: false,
videoState: $videoState,
isEvent: true,
mWidth: '100%',
mHeight: '100%'
})
.margin({
top: 15,
bottom: 15
})
if (this.isShowOperation) {
Row({ space: 10 }) {
Image($r('app.media.icon_back'))
.width(24)
.height(24)
.objectFit(ImageFit.Cover)
.onClick(() => {
router.back()
})
Text(this.name)
.fontSize(20)
.fontColor(Color.White)
}
.padding(20)
}
}.width('100%')
.height('40%')
.backgroundColor(Color.Black)
// 介紹
Column() {
Scroll(this.scrollerForScroll) {
Column() {
// 簡(jiǎn)介內(nèi)容
Column() {
// 標(biāo)題
Row() {
Text(this.name)
.fontColor(Color.Black)
.fontSize(26)
.width('88%')
Row() {
Text($r('app.string.introduce'))
.fontColor($r('app.color.introduce_text'))
.fontSize(16)
Image($r('app.media.icon_right'))
.width(16)
.height(20)
.objectFit(ImageFit.Contain)
}.onClick(() => {
console.info(`CLICK 設(shè)置前 isShowIntroduce ${this.isShowIntroduce}`)
this.isScrollClose = false
this.isShowIntroduce = true
console.info(`CLICK 設(shè)置后 isShowIntroduce ${this.isShowIntroduce}`)
})
}
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Bottom)
.width('100%')
.height('40')
.border({
width: 0,
color: Color.Gray
})
// 簡(jiǎn)介
Column() {
Row({ space: 15 }) {
Text($r('app.string.directs'))
.fontColor($r('app.color.introduce_title_text'))
.fontSize(16)
Text(this.directs)
.fontColor($r('app.color.introduce_text'))
.fontSize(14)
}.justifyContent(FlexAlign.Start)
.width('100%')
.margin({
top: 5,
bottom: 5
})
Text($r('app.string.actors'))
.fontColor($r('app.color.introduce_title_text'))
.fontSize(16)
.margin({
top: 5,
bottom: 5
})
.width('100%')
Text(this.actors)
.fontColor($r('app.color.introduce_text'))
.fontSize(14)
.width('100%')
.margin({
top: 5,
bottom: 5
})
Text(this.introduction)
.fontColor($r('app.color.introduce_text'))
.fontSize(16)
.width('100%')
.lineHeight(26)
.maxLines(2)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
.margin({
top: 5,
bottom: 5
})
}
.width('100%')
.height(150)
.justifyContent(FlexAlign.Start)
.margin({
top: 20,
bottom: 20
})
.border({
width: 0,
color: Color.Green
})
Text($r('app.string.guess_like'))
.fontColor(Color.Black)
.fontSize(18)
.width('100%')
}
.width('100%')
.height('280')
.border({
width: 0,
color: Color.Red
})
// 猜你喜歡
VideoListView({
videoList: $likeVideoList,
scrollIndex: $scrollIndex,
isBlackModule: true
})
}
}
.scrollBar(BarState.Off)
}
.width('95%')
.height('55%')
.backgroundColor(Color.White)
.padding(20)
.margin(30)
.border({
radius: 20
})
}.width('100%')
.height('100%')
// 電影簡(jiǎn)介彈窗
Panel(this.isShowIntroduce) {
VideoIntroduceView({
videoData: $mVideoData,
onClose: this.onCloseIntroduce.bind(this),
isScrollClose: this.isScrollClose
})
}
.type(PanelType.Foldable) // 內(nèi)容永久展示
.mode(PanelMode.Half)
.dragBar(false)
.halfHeight(500)
.onChange((width, height, mode) => {
console.info(`${TAG} Panel onChange ${JSON.stringify(mode)}`)
if (mode === PanelMode.Mini) {
this.isShowIntroduce = false
this.isScrollClose = true
}
})
}
.width('100%')
.height('100%')
.backgroundImage($r('app.media.main_bg'), ImageRepeat.XY)
}
}

  • VideoView:抽象出的視頻播放組件,用于播放視頻和控制視頻
  • 電影簡(jiǎn)介,外層使用Scroll封裝,其中包含了簡(jiǎn)介的基礎(chǔ)信息和推薦電影列表,由于外層是Scroll,電影推薦列表使用了Grid組件,存在滑動(dòng)沖突的問(wèn)題,解決方案參看:??OpenHarmony仿視頻播放器應(yīng)用-愛(ài)電影(二)??? 中的 “
    問(wèn)題1:Scroll與Grid列表嵌套時(shí),電影列表無(wú)法顯示完整,或者無(wú)法顯示banner”。
  • 電影簡(jiǎn)介詳情使用Panel容器封裝,??Panel??為可滑動(dòng)面板,顯示時(shí)從底部向上滑起,類似于抽屜組件。
  • VideoIntroduceView:自定義封裝的一個(gè)電影介紹的容器,容器內(nèi)部包括了詳細(xì)的電影簡(jiǎn)介。
  • VideoListView:猜你喜歡的模塊中,使用了電影列表控件,這個(gè)在上一篇也有提到。

2、VideoView.ets

視頻播放器組件,主要使用Video媒體容器組件實(shí)現(xiàn),Video不僅可以加載本地資源,也可以加載網(wǎng)絡(luò)資源,在加載網(wǎng)絡(luò)資源時(shí),首先需要在module.json5中添加ohos.permission.INTERNET權(quán)限,并且連接外網(wǎng),然后只需要替換Video的src屬性值為網(wǎng)絡(luò)地址即可。

/**
* 視頻播放器視圖
*/
import { PLAYBACK_SPEED, PLAYBACK_STATE } from '../model/Playback'
import { TimeUtils } from '../utils/TimeUtils'
import { VideoSpeed } from '../model/VideoSpeed'
import emitter from '@ohos.events.emitter';
import { CommonData } from '../model/CommonData'
const TAG: string = 'VideoView'
@Component
export struct VideoView {
@Prop _TAG: string
@Link videoUri: any
@Link previewUri: any
@Link videoRate: VideoSpeed
@Link videoRateIndex: number
@Prop mWidth: string
@Prop mHeight: string
private videoController: VideoController = new VideoController()
@Link videoState: string
@Consume('play_time') curTime: number
@State curTimeStr: string = '00:00:00'
@State durationCountStr: string = '00:00:00'
@State curSliderValue: number = 0
private durationNumber: number = 0
private selectSpeedOption: Array<SelectOption>
onScreen: (isFull: boolean) => void
@Prop isFullScreen: boolean
@State isAutoPlay: boolean = false // 是否自動(dòng)播放
private lastTime: number
isEvent: boolean // 是否需要注冊(cè)事件
@Consume('show_operation') isShowOperation: boolean // 是否顯示操作視圖
private timeID: number
aboutToAppear() {
console.info(`${TAG} ${this._TAG} aboutToAppear`)
if (this.isEvent) {
this.registerEmitter()
}
this.startTime()
// 初始化播放倍數(shù)
this.selectSpeedOption = new Array<SelectOption>()
for (const item of PLAYBACK_SPEED) {
let option: SelectOption = {
value: item.val
}
this.selectSpeedOption.push(option)
}
this.updateVideo(this.curTime, this.videoState, 'aboutToAppear')
}
updateVideo(time: number, state: string, tag: string) {
console.info(`${TAG} ${this._TAG} ${tag} updateVideo time: ${time} state: ${state}`)
if (state === PLAYBACK_STATE.START) {
console.info(`${TAG} ${this._TAG} updateVideo start`)
this.isAutoPlay = true
this.lastTime = time
this.vSetTime(time)
// this.videoController.start()
} else {
console.info(`${TAG} ${this._TAG} updateVideo stop`)
this.isAutoPlay = false
this.videoController.stop()
}
}
aboutToDisappear() {
console.info(`${TAG} ${this._TAG} aboutToDisappear`)
this.destroy()
}
registerEmitter() {
emitter.on({
eventId: CommonData.EVENT_PLAY_VIDEO
}, (event) => {
console.info(`${TAG} ${this._TAG} ${CommonData.EVENT_PLAY_VIDEO} callback : ${JSON.stringify(event)}`)
let params = event.data
if (params.hasOwnProperty('cur_time')) {
this.lastTime = params['cur_time']
console.info(`${TAG} ${this._TAG} Emitter getParams curTime: ${this.curTime}`)
}
if (params.hasOwnProperty('video_state')) {
this.videoState = params['video_state']
console.info(`${TAG} ${this._TAG} Emitter getParams curTime: ${this.videoState}`)
}
console.info(`${TAG} ${this._TAG} Emitter getParams curTime: ${this.videoState}`)
this.updateVideo(this.lastTime, this.videoState, 'emitter')
})
}
unregisterEmitter() {
emitter.off(CommonData.EVENT_PLAY_VIDEO)
}
vSetTime(time: number) {
console.info(`${TAG} ${this._TAG} vSetTime curTime: ${time}`)
this.videoController.setCurrentTime(time, SeekMode.Accurate)
}
clickStartOrPause() {
if (this.videoState === PLAYBACK_STATE.START) {
this.videoController.pause()
} else {
this.videoController.start()
}
}
startTime() {
if (this.timeID > 0) {
this.stopTime()
}
this.timeID = setTimeout(() => {
this.isShowOperation = false
}, 5000)
}
stopTime() {
clearTimeout(this.timeID)
this.timeID = -1
}
destroy() {
this.videoController.stop()
if (this.isEvent) {
this.unregisterEmitter()
}
this.stopTime()
}
build() {
Stack({
alignContent: Alignment.BottomStart
}) {
// 視頻播放
Video({
src: 'https://vd4.bdstatic.com/mda-jdmyw860sqcu8utw/sc/mda-jdmyw860sqcu8utw.mp4',
previewUri: this.previewUri,
currentProgressRate: this.videoRate.speed,
controller: this.videoController
})
.width('100%')
.backgroundColor('#000000')
.controls(false)
.autoPlay(this.isAutoPlay)
.objectFit(ImageFit.Contain)
.onTouch((event) => {
if (event.type === TouchType.Down) {
console.info(`${TAG} ${this._TAG} 視頻被點(diǎn)擊`)
this.isShowOperation = !this.isShowOperation
if (this.isShowOperation) {
this.startTime()
}
}
})
.onStart(() => {
console.info(`${TAG} ${this._TAG} 播放`)
this.videoState = PLAYBACK_STATE.START
})
.onPause(() => {
console.info(`${TAG} ${this._TAG} 暫停`)
this.videoState = PLAYBACK_STATE.PAUSE
})
.onFinish(() => {
console.info(`${TAG} ${this._TAG} 結(jié)束`)
this.videoState = PLAYBACK_STATE.FINISH
})
.onError(() => {
console.info(`${TAG} ${this._TAG} 播放失敗`)
this.videoState = PLAYBACK_STATE.ERROR
})
.onPrepared((callback) => {
// 視頻準(zhǔn)備完成時(shí)觸發(fā)該事件,通過(guò)duration可以獲取視頻時(shí)長(zhǎng),單位為秒(s)
this.durationNumber = callback.duration
this.durationCountStr = TimeUtils.FormatTime(this.durationNumber)
// console.info(`${TAG} onPrepared 視頻時(shí)長(zhǎng) ${this.durationCountStr} 原始值:${this.durationNumber}`)
})
.onSeeking((callback) => {
// 操作進(jìn)度條過(guò)程時(shí)上報(bào)時(shí)間信息,單位為s。
console.info(`${TAG} ${this._TAG} onSeeking ${callback.time}`)
})
.onUpdate((callback) => {
// 播放進(jìn)度變化時(shí)觸發(fā)該事件,單位為s,更新時(shí)間間隔為250ms。
if (this.lastTime > 0 && callback.time !== this.lastTime) {
console.info(`${TAG} ${this._TAG} onUpdate vSetTime curTime lastTime ${this.lastTime} callback time:${callback.time}`)
this.vSetTime(this.lastTime)
this.curTime = this.lastTime
this.lastTime = 0
} else {
this.curTime = callback.time
console.info(`${TAG} ${this._TAG} onUpdate curTime ${this.curTime}`)
}
this.curTimeStr = TimeUtils.FormatTime(this.curTime)
// console.info(`${TAG} onUpdate 視頻播放時(shí)間更新 ${this.curTimeStr} 原始值${callback.time}`)
this.curSliderValue = TimeUtils.Rounding(this.curTime * 100 / this.durationNumber)
// console.info(`${TAG} onUpdate 更新滑塊進(jìn)度 ${this.curSliderValue}`)
})
.onFullscreenChange((callback) => {
// 在全屏播放與非全屏播放狀態(tài)之間切換時(shí)觸發(fā)該事件,返回值為true表示進(jìn)入全屏播放狀態(tài),為false則表示非全屏播放。
console.info(`${TAG} ${this._TAG} onFullscreenChange ${callback.fullscreen}`)
})
if (this.isShowOperation) {
// 居中操作按鈕(播放/暫停)
Column() {
Image(this.videoState !== PLAYBACK_STATE.START ? $r('app.media.video_start_60') : $r('app.media.video_pause_60'))
.width(60)
.height(60)
.objectFit(ImageFit.Cover)
.onClick(() => {
this.clickStartOrPause()
})
}
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.width('100%')
.height('100%')
// 視頻操作欄
Row({
space: 10
}) {
// 播放/暫停按鈕
Image(this.videoState !== PLAYBACK_STATE.START ? $r('app.media.video_start') : $r('app.media.video_pause'))
.width(26)
.height(26)
.objectFit(ImageFit.Cover)
.onClick(() => {
this.clickStartOrPause()
})
// 播放時(shí)間
Text(this.curTimeStr)
.fontSize(10)
.fontColor(Color.White)
// 進(jìn)度條
Slider({
value: this.curSliderValue,
min: 0,
max: 100,
style: SliderStyle.OutSet
})
.showSteps(false)
.showTips(false)
.blockColor(Color.White)
.trackColor(Color.White)
.selectedColor('#36AD08')
.width('50%')
.onChange((value: number, mode: SliderChangeMode) => {
// SliderChangeMode Begin=0:開(kāi)始 Moving=1 End=2 Click=3
let timePercentage = value.toFixed(0)
console.info(`${TAG} Slider onChange ${value} ${timePercentage}`)
// 計(jì)算滑動(dòng)滑塊需要播放的時(shí)間
this.curTime = parseInt(timePercentage) * this.durationNumber / 100
this.vSetTime(this.curTime)
this.curTimeStr = TimeUtils.FormatTime(this.curTime)
console.info(`${TAG} ${this._TAG} Slider onChange 滑塊滑動(dòng)時(shí)間變更 curTime:${this.curTime} curTimeStr ${this.curTimeStr}`)
})
// 總時(shí)長(zhǎng)
Text(this.durationCountStr)
.fontSize(10)
.fontColor(Color.White)
Blank()
// 播放倍數(shù)
if (this.isFullScreen) {
Select(this.selectSpeedOption)
.selected(this.videoRateIndex)
.value(this.videoRate.val)
.font({ size: 10 })
.fontColor(Color.White)
.selectedOptionFont({ size: 10 })
.selectedOptionFontColor('#F54F02')
.optionFontColor('#5E5E5E')
.optionFont({ size: 10 })
.onSelect((index: number) => {
console.info('Select:' + index)
this.videoRate = PLAYBACK_SPEED[index]
this.videoRateIndex = index
console.info(`${TAG} videoRateIndex = ${this.videoRateIndex}`)
})
.border({
width: 0,
color: Color.White
})
}
// 浮動(dòng)層
Image($r('app.media.icon_float'))
.width(32)
.height(32)
.objectFit(ImageFit.Cover)
.onClick(() => {
console.info(`${TAG} 啟動(dòng)浮動(dòng)層`)
})
// 全屏切換
Image(this.isFullScreen ? $r('app.media.icon_small_screen') : $r('app.media.icon_full_screen'))
.width(26)
.height(26)
.objectFit(ImageFit.Cover)
.onClick(() => {
console.info(`${TAG} 全屏切換`)
this.onScreen(!this.isFullScreen)
})
}
.width('100%')
.height('60')
.backgroundImage($r('app.media.bg_control_1'), ImageRepeat.X)
.padding({
left: 20,
right: 20
})
}
}.width(this.mWidth)
.height(this.mHeight)
.backgroundColor(Color.Gray)
}
}

下面對(duì)以上代碼進(jìn)行說(shuō)明:

  • @Provide() @Consume() :這里的注解@Provide和@Consume,兩者需要配合使用。@Provide作為數(shù)據(jù)的提供方,可以更新其子孫節(jié)點(diǎn)的數(shù)據(jù),并觸發(fā)頁(yè)面渲染。@Consume在感知到@Provide數(shù)據(jù)的更新后,會(huì)觸發(fā)當(dāng)前自定義組件的重新渲染。
    | @Consume(‘play_time’) curTime:用于向全屏播放時(shí)同步當(dāng)前播放的時(shí)間節(jié)點(diǎn),在全屏播放時(shí)可以繼續(xù)播放。
    | @Consume(‘show_operation’) isShowOperation: 用于控制播放器上層視圖(暫停、播放、進(jìn)度、時(shí)間、最大化)的顯示和隱藏。
  • Video中有onStart、onPause、onFinish、onError、onPrepared、onSeeking、onUpdate 函數(shù)用于監(jiān)聽(tīng)視頻播放的狀態(tài)
  • Slider:滑動(dòng)條組件,用于顯示和控制視頻播放的進(jìn)度,用戶可以移動(dòng)滑塊來(lái)控制視頻播放的進(jìn)度。
  • Select:下拉選擇菜單,用于選擇視頻的倍數(shù),在全屏播放時(shí)使用。

3、VideoIntroduceView.ets

詳細(xì)的電影介紹,包括電影的相關(guān)信息,演員列表,演員圖片、電影的劇情。

import { VideoData, User } from '../model/VideoData'
/**
* 電影簡(jiǎn)介
*/
const TAG: string = 'VideoIntroduceView'
@Component
export struct VideoIntroduceView {
@Link videoData: VideoData
onClose: () => void
@Prop @Watch('scrollClose') isScrollClose: boolean // 是否為滾動(dòng)關(guān)閉
private userList: Array<User> = []
private scroller: Scroller = new Scroller()
aboutToAppear() {
console.info(`${TAG} aboutToAppear`)
// 初始化演員數(shù)據(jù)
if (this.videoData) {
// 先添加導(dǎo)演
for (const item of this.videoData.directs) {
this.userList.push(item)
}
// 再添加演員
for (const item of this.videoData.actors) {
this.userList.push(item)
}
}
}
scrollClose() {
if (this.isScrollClose) {
this.scroller.scrollEdge(Edge.Top)
}
}
aboutToDisappear() {
this.userList = []
}
build() {
Scroll(this.scroller) {
Column() {
Row() {
// 簡(jiǎn)介標(biāo)題
Text($r('app.string.introduce'))
.fontSize(22)
.fontColor(Color.Black)
.width('95%')
Column() {
Image($r('app.media.icon_close'))
.width(22)
.height(22)
.objectFit(ImageFit.Cover)
}.width(32)
.height(32)
.onClick(() => {
console.info(`${TAG} CLOSE onClose`)
this.scroller.scrollEdge(Edge.Top)
this.onClose()
})
}.justifyContent(FlexAlign.Start)
.width('100%')
.margin({
top: 10,
bottom: 10
})
// 電影基礎(chǔ)信息
Row({ space: 20 }) {
Image(this.videoData.image)
.width(105)
.height(135)
.objectFit(ImageFit.Cover)
Column({ space: 10 }) {
Text(this.videoData.name)
.fontSize(18)
.fontColor('#2A1818')
.align(Alignment.Start)
.width('300')
Row() {
Text(this.videoData.describe)
.fontSize(14)
.fontColor(Color.White)
.backgroundColor('#C4C4C4')
.padding({
top: 5,
bottom: 5,
left: 10,
right: 10
})
Blank()
}.width('300')
Text(this.videoData.resourceType)
.fontSize(18)
.fontColor('#5E5E5E')
.width(300)
Row({ space: 5 }) {
Image($r('app.media.icon_source'))
.width(16)
.height(16)
.objectFit(ImageFit.Cover)
Text(this.videoData.source)
.fontSize(18)
.fontColor('#5E5E5E')
.width(200)
}
.width(300)
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Start)
}
.justifyContent(FlexAlign.Start)
}
.width('100%')
.height(135)
.justifyContent(FlexAlign.Start)
.margin({
top: 10,
bottom: 10
})
// 評(píng)分
Row({ space: 20 }) {
Column({ space: 0 }) {
Text(this.videoData.grade)
.fontSize(35)
.fontColor('#F54F02')
Text(this.videoData.gradeNumber + '人參與評(píng)分')
.fontSize(12)
.fontColor('#868686')
}.width('120')
Row({ space: 10 }) {
Image($r('app.media.icon_fire'))
.width(16)
.height(16)
.objectFit(ImageFit.Cover)
Text($r('app.string.heat_value'))
.fontSize(14)
.fontColor('#F54F02')
Progress({
value: this.videoData.heat,
type: ProgressType.Linear,
total: 100
})
.width('40%')
.color('#F54F02')
.backgroundColor('#868686')
.style({
strokeWidth: 10
})
}
}
.width('100%')
.justifyContent(FlexAlign.Start)
.margin({
top: 10,
bottom: 10
})
// 演職員
Text($r('app.string.cast'))
.fontSize(18)
.fontColor('#2A1818')
.width('100%')
.margin({
top: 10,
bottom: 10
})
// 演員列表
Row() {
List({
space: 10,
initialIndex: 0
}) {
ForEach(this.userList, (item: User) => {
ListItem() {
Column({ space: 10 }) {
Image(item.icon)
.width(105)
.height(135)
.objectFit(ImageFit.Cover)
Text(item.name)
.width(105)
.fontSize(14)
.fontColor('#2A1818')
Text(item.role)
.width(105)
.fontSize(12)
.fontColor('#868686')
}
}
})
}
.scrollBar(BarState.Off)
.listDirection(Axis.Horizontal)
.cachedCount(3)
}.width('100%')
.justifyContent(FlexAlign.Start)
.margin({
top: 10,
bottom: 10
})
// 劇情
Text($r('app.string.story'))
.fontSize(18)
.fontColor('#2A1818')
.width('100%')
.margin({
top: 10,
bottom: 10
})
Text(this.videoData.introduction)
.fontSize(12)
.fontColor('#868686')
.margin({
top: 10,
bottom: 10
})
}
.padding({
top: 20,
bottom: 20,
left: 40,
right: 40
})
.justifyContent(FlexAlign.Start)
}.width('100%')
.scrollBar(BarState.Off)
.backgroundColor(Color.White)
.border({
radius: 20
})
}
}

電影簡(jiǎn)介中并無(wú)比較復(fù)雜的內(nèi)容,主要是根據(jù)UI設(shè)計(jì)將各組件進(jìn)行布置即可,這里主要講講電影簡(jiǎn)介外層的容器—??Panel??,可滑動(dòng)面板,是一種用于內(nèi)容展示的窗口,窗口的尺寸可以切換,根據(jù)PanelType的不同,可以分為三種不同類型的屏幕,大(類全屏)、中(類半屏)、小,具體說(shuō)明如下:

PanelType枚舉說(shuō)明:

名稱

描述

Minibar

提供minibar和類全屏展示切換效果。

Foldable

內(nèi)容永久展示類,提供大(類全屏)、中(類半屏)、小三種尺寸展示切換效果。

Temporary

內(nèi)容臨時(shí)展示區(qū),提供大(類全屏)、中(類半屏)兩種尺寸展示切換效果。

可滑動(dòng)面板的初始狀態(tài)有三種:

PanelMode枚舉說(shuō)明:

名稱

描述

Mini

類型為minibar和foldable時(shí),為最小狀態(tài);類型為temporary,則不生效。

Half

類型為foldable和temporary時(shí),為類半屏狀態(tài);類型為minibar,則不生效。

Full

類全屏狀態(tài)。

在這里Panel的type為PanelType.Foldable,mode為PanelMode.Half,也就是初始為半屏顯示,同時(shí)設(shè)置了半屏的高度halfHeight(500)。相關(guān)屬性詳解如下:

屬性:

名稱

參數(shù)類型

描述

type

PanelType

設(shè)置可滑動(dòng)面板的類型。<br/>默認(rèn)值:PanelType.Foldable

mode

PanelMode

設(shè)置可滑動(dòng)面板的初始狀態(tài)。

dragBar

boolean

設(shè)置是否存在dragbar,true表示存在,false表示不存在。<br/>默認(rèn)值:true

fullHeight

string | number

指定PanelMode.Full狀態(tài)下的高度。

halfHeight

string | number

指定PanelMode.Half狀態(tài)下的高度,默認(rèn)為屏幕尺寸的一半。

miniHeight

string | number

指定PanelMode.Mini狀態(tài)下的高度。

show

boolean

當(dāng)滑動(dòng)面板彈出時(shí)調(diào)用。

backgroundMask<sup>9+</sup>

ResourceColor

指定Panel的背景蒙層。

Panel組件向下滑動(dòng)的時(shí)候會(huì)進(jìn)入PanelModeMini狀態(tài),此狀態(tài)中滑塊不會(huì)完全消失在界面上,而是需要?jiǎng)討B(tài)的設(shè)置Panel構(gòu)造函數(shù)來(lái)控制組件的顯隱。

Panel(show:boolean)

參數(shù):

參數(shù)名

參數(shù)類型

必填

參數(shù)描述

show

boolean

控制Panel顯示或隱藏。

那我們?nèi)绾沃笇?dǎo)Panel的變化呢,主要是通過(guò)Panel的監(jiān)聽(tīng)事件onChange(),通過(guò)回調(diào)函數(shù)中的PanelMode參數(shù)判斷當(dāng)前Panel的狀態(tài)。

事件:

名稱

功能描述

onChange(event: (width: number, height: number, mode: PanelMode) => void)

當(dāng)可滑動(dòng)面板發(fā)生狀態(tài)變化時(shí)觸發(fā), 返回的height值為內(nèi)容區(qū)高度值,當(dāng)dragbar屬性為true時(shí),panel本身的高度值為dragbar高度加上內(nèi)容區(qū)高度。

這里整個(gè)視頻播放的主要內(nèi)容就講解完了,下一篇我們繼續(xù)講解視頻全屏播放頁(yè)面的實(shí)現(xiàn)。

??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??

??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??

??https://ost.51cto.com??

責(zé)任編輯:jianghua 來(lái)源: 51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)
相關(guān)推薦

2023-03-28 09:38:34

開(kāi)發(fā)應(yīng)用鴻蒙

2023-03-28 09:44:02

開(kāi)發(fā)應(yīng)用鴻蒙

2023-03-29 09:37:49

視頻播放器應(yīng)用鴻蒙

2022-08-16 17:37:06

視頻播放器鴻蒙

2015-09-01 16:48:44

ios暴風(fēng)視頻播放器

2011-07-20 16:21:20

iPhone 視頻 播放器

2015-05-21 15:25:42

VLC播放器

2012-06-04 13:44:08

2015-01-22 15:44:55

Android源碼音樂(lè)播放器

2022-06-21 14:41:38

播放器適配西瓜視頻

2021-10-19 14:27:07

鴻蒙HarmonyOS應(yīng)用

2021-10-21 16:00:07

鴻蒙HarmonyOS應(yīng)用

2022-11-12 08:26:04

VLC視頻播放器裁剪視頻

2018-05-25 14:37:58

2022-01-27 08:12:50

Potplayer播放器

2023-03-06 16:20:08

視頻播放器VLC

2023-08-26 19:07:40

VLC旋轉(zhuǎn)視頻

2011-06-13 09:33:04

2015-01-19 13:52:38

Android源碼多功能播放器

2011-06-27 11:23:21

Qt 音樂(lè)播放器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)