精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

OpenHarmony仿視頻播放器應用-愛電影(三)

系統 OpenHarmony
本篇我們詳細的說說電影播放頁面開發涉及的內容,首先我們來看下電影播放頁面的設計圖。

??想了解更多關于開源的內容,請訪問:??

??51CTO 開源基礎軟件社區??

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

效果

??在線視頻??

接??上一篇??,主頁上顯示了電影資源,點擊你想看的電影會跳轉至電影播放頁面,接下來我們詳細的說說電影播放頁面開發涉及的內容,首先我們來看下電影播放頁面的設計圖,如下:

#創作者激勵#OpenHarmony仿視頻播放器應用-愛電影(三)-開源基礎軟件社區

#創作者激勵#OpenHarmony仿視頻播放器應用-愛電影(三)-開源基礎軟件社區

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

項目開發

開發環境

硬件平臺:DAYU2000 RK3568
系統版本: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

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

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'
/**
* 視頻播放頁面
*/
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 // 是否顯示簡介
@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()
// 獲取當前需要播放的電影資源信息
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, // 當前播放時間
video_state: this.videoState // 播放狀態
}
})
}
}
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() {
// 簡介內容
Column() {
// 標題
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 設置前 isShowIntroduce ${this.isShowIntroduce}`)
this.isScrollClose = false
this.isShowIntroduce = true
console.info(`CLICK 設置后 isShowIntroduce ${this.isShowIntroduce}`)
})
}
.justifyContent(FlexAlign.Start)
.alignItems(VerticalAlign.Bottom)
.width('100%')
.height('40')
.border({
width: 0,
color: Color.Gray
})
// 簡介
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%')
// 電影簡介彈窗
Panel(this.isShowIntroduce) {
VideoIntroduceView({
videoData: $mVideoData,
onClose: this.onCloseIntroduce.bind(this),
isScrollClose: this.isScrollClose
})
}
.type(PanelType.Foldable) // 內容永久展示
.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:抽象出的視頻播放組件,用于播放視頻和控制視頻
  • 電影簡介,外層使用Scroll封裝,其中包含了簡介的基礎信息和推薦電影列表,由于外層是Scroll,電影推薦列表使用了Grid組件,存在滑動沖突的問題,解決方案參看:??OpenHarmony仿視頻播放器應用-愛電影(二)??? 中的 “
    問題1:Scroll與Grid列表嵌套時,電影列表無法顯示完整,或者無法顯示banner”。
  • 電影簡介詳情使用Panel容器封裝,??Panel??為可滑動面板,顯示時從底部向上滑起,類似于抽屜組件。
  • VideoIntroduceView:自定義封裝的一個電影介紹的容器,容器內部包括了詳細的電影簡介。
  • VideoListView:猜你喜歡的模塊中,使用了電影列表控件,這個在上一篇也有提到。

2、VideoView.ets

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

/**
* 視頻播放器視圖
*/
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 // 是否自動播放
private lastTime: number
isEvent: boolean // 是否需要注冊事件
@Consume('show_operation') isShowOperation: boolean // 是否顯示操作視圖
private timeID: number
aboutToAppear() {
console.info(`${TAG} ${this._TAG} aboutToAppear`)
if (this.isEvent) {
this.registerEmitter()
}
this.startTime()
// 初始化播放倍數
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} 視頻被點擊`)
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} 結束`)
this.videoState = PLAYBACK_STATE.FINISH
})
.onError(() => {
console.info(`${TAG} ${this._TAG} 播放失敗`)
this.videoState = PLAYBACK_STATE.ERROR
})
.onPrepared((callback) => {
// 視頻準備完成時觸發該事件,通過duration可以獲取視頻時長,單位為秒(s)
this.durationNumber = callback.duration
this.durationCountStr = TimeUtils.FormatTime(this.durationNumber)
// console.info(`${TAG} onPrepared 視頻時長 ${this.durationCountStr} 原始值:${this.durationNumber}`)
})
.onSeeking((callback) => {
// 操作進度條過程時上報時間信息,單位為s。
console.info(`${TAG} ${this._TAG} onSeeking ${callback.time}`)
})
.onUpdate((callback) => {
// 播放進度變化時觸發該事件,單位為s,更新時間間隔為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 視頻播放時間更新 ${this.curTimeStr} 原始值${callback.time}`)
this.curSliderValue = TimeUtils.Rounding(this.curTime * 100 / this.durationNumber)
// console.info(`${TAG} onUpdate 更新滑塊進度 ${this.curSliderValue}`)
})
.onFullscreenChange((callback) => {
// 在全屏播放與非全屏播放狀態之間切換時觸發該事件,返回值為true表示進入全屏播放狀態,為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()
})
// 播放時間
Text(this.curTimeStr)
.fontSize(10)
.fontColor(Color.White)
// 進度條
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:開始 Moving=1 End=2 Click=3
let timePercentage = value.toFixed(0)
console.info(`${TAG} Slider onChange ${value} ${timePercentage}`)
// 計算滑動滑塊需要播放的時間
this.curTime = parseInt(timePercentage) * this.durationNumber / 100
this.vSetTime(this.curTime)
this.curTimeStr = TimeUtils.FormatTime(this.curTime)
console.info(`${TAG} ${this._TAG} Slider onChange 滑塊滑動時間變更 curTime:${this.curTime} curTimeStr ${this.curTimeStr}`)
})
// 總時長
Text(this.durationCountStr)
.fontSize(10)
.fontColor(Color.White)
Blank()
// 播放倍數
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
})
}
// 浮動層
Image($r('app.media.icon_float'))
.width(32)
.height(32)
.objectFit(ImageFit.Cover)
.onClick(() => {
console.info(`${TAG} 啟動浮動層`)
})
// 全屏切換
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)
}
}

下面對以上代碼進行說明:

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

3、VideoIntroduceView.ets

詳細的電影介紹,包括電影的相關信息,演員列表,演員圖片、電影的劇情。

import { VideoData, User } from '../model/VideoData'
/**
* 電影簡介
*/
const TAG: string = 'VideoIntroduceView'
@Component
export struct VideoIntroduceView {
@Link videoData: VideoData
onClose: () => void
@Prop @Watch('scrollClose') isScrollClose: boolean // 是否為滾動關閉
private userList: Array<User> = []
private scroller: Scroller = new Scroller()
aboutToAppear() {
console.info(`${TAG} aboutToAppear`)
// 初始化演員數據
if (this.videoData) {
// 先添加導演
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() {
// 簡介標題
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
})
// 電影基礎信息
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
})
// 評分
Row({ space: 20 }) {
Column({ space: 0 }) {
Text(this.videoData.grade)
.fontSize(35)
.fontColor('#F54F02')
Text(this.videoData.gradeNumber + '人參與評分')
.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
})
}
}

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

PanelType枚舉說明:

名稱

描述

Minibar

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

Foldable

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

Temporary

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

可滑動面板的初始狀態有三種:

PanelMode枚舉說明:

名稱

描述

Mini

類型為minibar和foldable時,為最小狀態;類型為temporary,則不生效。

Half

類型為foldable和temporary時,為類半屏狀態;類型為minibar,則不生效。

Full

類全屏狀態。

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

屬性:

名稱

參數類型

描述

type

PanelType

設置可滑動面板的類型。<br/>默認值:PanelType.Foldable

mode

PanelMode

設置可滑動面板的初始狀態。

dragBar

boolean

設置是否存在dragbar,true表示存在,false表示不存在。<br/>默認值:true

fullHeight

string | number

指定PanelMode.Full狀態下的高度。

halfHeight

string | number

指定PanelMode.Half狀態下的高度,默認為屏幕尺寸的一半。

miniHeight

string | number

指定PanelMode.Mini狀態下的高度。

show

boolean

當滑動面板彈出時調用。

backgroundMask<sup>9+</sup>

ResourceColor

指定Panel的背景蒙層。

Panel組件向下滑動的時候會進入PanelModeMini狀態,此狀態中滑塊不會完全消失在界面上,而是需要動態的設置Panel構造函數來控制組件的顯隱。

Panel(show:boolean)

參數:

參數名

參數類型

必填

參數描述

show

boolean

控制Panel顯示或隱藏。

那我們如何指導Panel的變化呢,主要是通過Panel的監聽事件onChange(),通過回調函數中的PanelMode參數判斷當前Panel的狀態。

事件:

名稱

功能描述

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

當可滑動面板發生狀態變化時觸發, 返回的height值為內容區高度值,當dragbar屬性為true時,panel本身的高度值為dragbar高度加上內容區高度。

這里整個視頻播放的主要內容就講解完了,下一篇我們繼續講解視頻全屏播放頁面的實現。

??想了解更多關于開源的內容,請訪問:??

??51CTO 開源基礎軟件社區??

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

責任編輯:jianghua 來源: 51CTO 開源基礎軟件社區
相關推薦

2023-03-28 09:44:02

開發應用鴻蒙

2023-03-28 09:38:34

開發應用鴻蒙

2023-03-29 09:37:49

視頻播放器應用鴻蒙

2022-08-16 17:37:06

視頻播放器鴻蒙

2015-09-01 16:48:44

ios暴風視頻播放器

2015-05-21 15:25:42

VLC播放器

2011-07-20 16:21:20

iPhone 視頻 播放器

2012-06-04 13:44:08

2015-01-22 15:44:55

Android源碼音樂播放器

2022-06-21 14:41:38

播放器適配西瓜視頻

2021-10-21 16:00:07

鴻蒙HarmonyOS應用

2021-10-19 14:27:07

鴻蒙HarmonyOS應用

2022-11-12 08:26:04

VLC視頻播放器裁剪視頻

2018-05-25 14:37:58

2022-01-27 08:12:50

Potplayer播放器

2023-08-26 19:07:40

VLC旋轉視頻

2023-03-06 16:20:08

視頻播放器VLC

2011-06-13 09:33:04

2015-01-19 13:52:38

Android源碼多功能播放器

2011-06-27 11:23:21

Qt 音樂播放器
點贊
收藏

51CTO技術棧公眾號

伦理一区二区三区| 好了av在线| 秋霞国产午夜精品免费视频| 精品国产一区久久久| 日本女人黄色片| 午夜影院在线观看国产主播| 国产精品天干天干在观线 | 国产一区二区av在线| 亚洲午夜电影网| 亚洲 国产 日韩 综合一区| 国产不卡av在线播放| 亚洲免费网址| 色综合男人天堂| 国产精品av久久久久久无| 欧美视频精品全部免费观看| 欧美日韩精品在线| 久久av秘一区二区三区| 四虎影视精品成人| 国产中文字幕精品| 日本一区二区三区四区视频| 欧美成人一二三区| 日本一二区不卡| 日韩精品专区在线影院观看| 波多野结衣天堂| 国模私拍视频在线播放| 国产欧美精品一区| 精品国产二区在线| 成人激情四射网| 日韩电影在线观看电影| 91av国产在线| 国产精品变态另类虐交| 97在线精品| 一本色道久久88综合日韩精品 | 久久久综合九色合综国产精品| 亚洲a级在线播放观看| 波多野结衣人妻| 六月丁香综合| 91av在线网站| 日本系列第一页| 91精品电影| 日韩有码在线观看| 香蕉视频久久久| 亚洲人成亚洲精品| 日韩av综合网| 五十路六十路七十路熟婆| 国产一区二区三区视频在线| 欧美午夜精品电影| 无码精品a∨在线观看中文| 国产蜜臀一区二区打屁股调教| 亚洲欧洲成人自拍| 亚洲欧美在线网| jzzjzzjzz亚洲成熟少妇| 久久久91精品国产一区二区精品| 精品无码久久久久国产| 成人免费视频国产| 夫妻av一区二区| 99久久伊人精品影院| 国产熟女精品视频| 国产综合色视频| 国产专区欧美专区| 一起草av在线| 九九精品视频在线看| 国产精品露脸av在线| 乱子伦一区二区三区| 日日摸夜夜添夜夜添精品视频 | 日韩国产欧美精品一区二区三区| 95视频在线观看| 国内精品国产成人国产三级粉色 | 精品女厕一区二区三区| 18岁网站在线观看| 中文在线а√在线8| 日本韩国一区二区三区| 三级视频中文字幕| 国产精久久久| 欧美变态tickling挠脚心| 污污免费在线观看| 偷拍视屏一区| 深夜福利国产精品| 黄色一级片中国| 亚洲乱码久久| 国产激情综合五月久久| 91极品身材尤物theporn| 国产乱妇无码大片在线观看| 国产v亚洲v天堂无码| 天堂中文在线资| 国产精品久久一级| 青青视频免费在线| 狼人综合视频| 欧美日本在线播放| 久久国产免费视频| 美女亚洲一区| 欧美成人h版在线观看| 国产精品500部| 麻豆freexxxx性91精品| 国产精品裸体一区二区三区| 你懂的好爽在线观看| 亚洲欧美激情在线| 欧美丰满熟妇bbbbbb百度| 粉嫩av一区二区三区四区五区 | 懂色av影视一区二区三区| 亚洲精品中文字幕无码蜜桃| 玖玖精品一区| 亚洲欧美日韩区| 玖玖爱这里只有精品| 久久精品五月| 产国精品偷在线| 国产黄色片在线观看| 亚洲伊人伊色伊影伊综合网| 无码人妻丰满熟妇区五十路百度| 精品国产三区在线| 亚洲人精品午夜在线观看| 欧美黑人一级片| 全部av―极品视觉盛宴亚洲| 韩国成人一区| 最新国产在线拍揄自揄视频| 欧美艳星brazzers| 日本一卡二卡在线| 国产大片一区| 国产精品18久久久久久麻辣| 欧美一级在线免费观看 | 国产三级中文字幕| 欧美电影免费观看| 亚洲韩国日本中文字幕| 亚洲国产精品免费在线观看| 蜜桃视频在线观看一区二区| 精品日韩美女| 97人人爽人人澡人人精品| 欧美日本韩国一区| 一区二区三区伦理片| 亚洲激情偷拍| 懂色一区二区三区av片| 日本中文字幕在线2020| 色激情天天射综合网| yjizz视频| 韩国自拍一区| 69堂成人精品视频免费| 麻豆免费在线观看| 欧美人牲a欧美精品| 丰满少妇高潮一区二区| 国产精品普通话对白| 成人免费91在线看| 青春草视频在线| 日韩一级黄色片| 极品久久久久久| 国产精品综合一区二区| 中文字幕超清在线免费观看| 亚洲91在线| 色偷偷综合社区| 中文字幕在线日亚洲9| 国产亚洲视频系列| 成人性视频欧美一区二区三区| 欧美成人基地| 57pao成人永久免费视频| 婷婷在线观看视频| 欧美视频不卡中文| 给我看免费高清在线观看| 一本久久知道综合久久| 久久香蕉综合色| 日韩在线观看不卡| 色哟哟入口国产精品| 在线观看国产精品视频| 国产精品久久一卡二卡| 五月天国产视频| 欧美欧美全黄| 国产女人水真多18毛片18精品| free性欧美| 国产视频亚洲视频| 男操女视频网站| 国产精品不卡一区二区三区| 伊人五月天婷婷| 在线视频观看日韩| 国产欧美日韩一区二区三区| 色女人在线视频| 日韩经典第一页| 免费一级a毛片| 最新不卡av在线| 国产精品久久久久久亚洲色| 亚洲一区成人| 伊人久久大香线蕉综合75| 精品成人18| 欧美一级免费看| 黄网站app在线观看| 精品乱码亚洲一区二区不卡| 国产精品男女视频| 国产精品视频九色porn| 黄页网站在线看| 久久99伊人| 青少年xxxxx性开放hg| 一区二区三区欧洲区| 日本精品性网站在线观看| 黄色网址在线免费| 日韩电影中文字幕| 国产特级aaaaaa大片| 五月开心婷婷久久| 国产又色又爽又高潮免费| 成人国产一区二区三区精品| 香蕉视频禁止18| 在线成人黄色| 一级特黄录像免费播放全99| 果冻天美麻豆一区二区国产| 国产精品美乳在线观看| a√中文在线观看| 中文字幕精品一区二区精品| 黄色www视频| 欧美日韩午夜在线| 99精品视频99| 依依成人综合视频| 人妻精品久久久久中文| 成人动漫中文字幕| 亚洲一区二区在线视频观看| 西西人体一区二区| 伊人再见免费在线观看高清版 | 一区二区在线视频| 天天操天天干天天爱| 91精品国产综合久久久久久| 欧美成人一区二区三区四区| 亚洲va韩国va欧美va精品| 国产喷水在线观看| 国产人成亚洲第一网站在线播放| 18禁一区二区三区| 激情六月婷婷综合| 五月婷婷六月合| 免播放器亚洲| 国产欧美日韩网站| 伊人久久大香线| 亚洲一区二区三区免费看| 人人网欧美视频| 国产日韩欧美一区二区三区四区| 亚洲免费黄色网| 一区二区电影在线观看| 日韩欧美99| 欧美偷窥清纯综合图区| 动漫一区二区在线| 日韩精品视频中文字幕| 国产拍精品一二三| 成人看片毛片免费播放器| 日本亚洲欧美三级| 香蕉久久免费电影| 日韩美女免费视频| 在线免费av资源| 欧美一级淫片播放口| 忘忧草在线日韩www影院| 午夜精品一区二区三区视频免费看| av官网在线播放| 美女av一区二区| 69xxx在线| 欧美风情在线观看| 亚洲欧美成人影院| 欧美激情欧美激情| 免费污视频在线观看| 欧美精品videos性欧美| 2019中文字幕在线电影免费| 韩剧1988在线观看免费完整版| 成人免费高清观看| 隔壁老王国产在线精品| 岛国av在线播放| 欧美一区二区.| 日韩精品99| 国产欧美精品在线| 国产人与zoxxxx另类91| 99www免费人成精品| 国产一级成人av| 欧美凹凸一区二区三区视频| 视频一区在线观看| 亚洲欧美日韩在线综合 | 佐佐木明希av| 欧美日本二区| 久久成人免费观看| 久久免费国产| 在线播放av中文字幕| 国产成人欧美日韩在线电影| 亚洲欧美综合视频| 久久免费视频色| 欧美xxxooo| 亚洲一区二区三区在线| 中文字幕亚洲精品在线| 欧美在线免费观看亚洲| 国产免费一区二区三区最新不卡 | 亚洲综合日韩在线| 免费萌白酱国产一区二区三区| 奇米精品在线| 911久久香蕉国产线看观看| 欧美一级片免费播放| 老司机精品视频网站| 中文字幕第66页| 91老师国产黑色丝袜在线| 成人免费视频入口| 亚洲线精品一区二区三区| 波多野结衣在线电影| 日韩女优制服丝袜电影| 久久电影视频| 欧美成人亚洲成人| 成人欧美magnet| 亚洲综合精品一区二区| 深爱激情综合| 无码粉嫩虎白一线天在线观看 | 国产精品免费网站| 亚洲精品一区二区三区在线| 日本一区二区免费看| 欧美久久成人| 国产一区二区在线免费播放| 国产成人亚洲精品狼色在线| 精品人妻无码一区二区三区换脸 | 狠狠人妻久久久久久综合| 欧美剧在线免费观看网站| 亚洲av片在线观看| 欧美成人午夜激情在线| 欧美性理论片在线观看片免费 | 亚洲欧美日本伦理| 亚洲天堂第一区| 日韩国产一区二| 亚州av综合色区无码一区| 国产精品久久久久久妇女6080 | 另类天堂av| 亚洲精品乱码久久久久久蜜桃欧美| 亚洲国产精品t66y| 特级毛片www| 亚洲第一av在线| 黄页视频在线播放| 国产精品视频在线观看| 免费看av成人| 91视频 -- 69xx| 成人久久久精品乱码一区二区三区 | 成人手机在线视频| 久久久久亚洲AV成人| 欧美日韩久久不卡| 风间由美一区| 国产999在线观看| 久久国产精品色av免费看| 日韩中文字幕亚洲精品欧美| 久久电影网电视剧免费观看| 乐播av一区二区三区| 欧美日韩亚洲网| 天堂av在线免费| 欧美精品久久久久a| 日韩精品一区二区三区中文字幕 | 在线观看三级视频欧美| 欧美zozo| 日本国产高清不卡| 中国av一区| 久久综合久久色| 久久久精品国产免大香伊| 你懂的国产在线| 日韩第一页在线| 中文字幕 在线观看| 欧美中日韩一区二区三区| 欧美中文日韩| 免费看污片网站| 欧美视频三区在线播放| 最新真实国产在线视频| 成人福利在线观看| 亚洲精品小说| 中国特级黄色片| 亚洲图片欧美综合| 日韩一区二区三区不卡| 91精品国产高清久久久久久| 日韩a级大片| 成年人免费在线播放| 国产欧美精品一区二区色综合朱莉| 最近中文字幕在线观看视频| 中文字幕久久久| 亚洲精品aaa| 精品免费久久久久久久| 成人网男人的天堂| 日本a级c片免费看三区| 一本色道久久综合狠狠躁篇怎么玩| www.久久| 欧美 亚洲 视频| 99精品在线免费| 免费精品一区二区| 久久在线免费观看视频| 成人性生交大片免费看中文视频| 人人妻人人添人人爽欧美一区| 久久九九久精品国产免费直播| 亚洲视频在线观看免费视频| 久久人人爽亚洲精品天堂| www.丝袜精品| 黄色三级视频片| 亚洲美女免费在线| 艳母动漫在线看| 国产日韩在线看| 激情久久久久久久| 一级特黄曰皮片视频| 日韩久久久久久| 91精品影视| 特级西西444| 国产婷婷精品av在线| 国产成人精品亚洲精品色欲| 欧美中文在线观看国产| 久久福利综合| 国产精品无码毛片| 欧美精品一二三区| 国产社区精品视频| 伊人久久99| 97久久超碰国产精品| 97人妻人人澡人人爽人人精品 | 99亚洲国产精品| 久久午夜色播影院免费高清| 国产日韩在线观看一区| 日产精品久久久一区二区福利|