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

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

系統(tǒng) OpenHarmony
簡單來說頁面分成上下兩部分,上半部分是一個橫向滾動的banner,下半部分是電影資源的列表,列表中的一行兩列均分,每一個資源信息包括:電影資源的宣傳圖、電影名稱、演員、電影亮點。

??想了解更多關(guān)于開源的內(nèi)容,請訪問:??

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

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

效果

??在線視頻??

接??上一篇??,閃屏頁面跳轉(zhuǎn)到主頁,接下來我們詳細(xì)的說說主頁開發(fā)涉及的內(nèi)容,首先我們來看下主頁是設(shè)計圖,如下:

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

簡單來說頁面分成上下兩部分,上半部分是一個橫向滾動的banner,下半部分是電影資源的列表,列表中的一行兩列均分,每一個資源信息包括:電影資源的宣傳圖、電影名稱、演員、電影亮點。

項目開發(fā)

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

硬件平臺: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

程序代碼

Index.ets

import { VideoDataSource } from '../model/VideoDataSource'
import { VideoData } from '../model/VideoData'
import { MockVideoData } from '../model/MockVideoData'
import router from '@ohos.router';
import { VideoListView } from '../view/VideoListView'
const TAG: string = 'Splash Index'
@Entry
@Component
struct Index {
@State bannerList: Array<VideoData> = []
@State videoList: Array<VideoData> = []
private scrollerForScroll: Scroller = new Scroller()
@State @Watch('scrollChange') scrollIndex: number = 0
@State opacity1: number = 0
aboutToAppear() {
this.initData()
router.clear()
}
scrollChange() {
if (this.scrollIndex === 0) {
this.scrollToAnimation(0, 0)
} else if (this.scrollIndex === 2) {
this.scrollToAnimation(0, 300)
}
}
scrollToAnimation(xOffset, yOffset) {
this.scrollerForScroll.scrollTo({
xOffset: xOffset,
yOffset: yOffset,
animation: {
duration: 3000,
curve: Curve.FastOutSlowIn
}
})
}
initData() {
this.bannerList = MockVideoData.getBannerList()
this.videoList = MockVideoData.getVideoList()
}
build() {
Column() {
Scroll(this.scrollerForScroll) {
Column() {
// banner
Swiper() {
LazyForEach(new VideoDataSource(this.bannerList), (item: VideoData) => {
Image(item.image)
.width('100%')
.height('100%')
.border({
radius: 20
})
.onClick(() => {
router.pushUrl({ url: 'pages/Playback',
params: {
video_data: item
} })
})
.objectFit(ImageFit.Fill)
}, item => item.id)
}
.width('100%')
.height(240)
.itemSpace(20)
.autoPlay(true)
.indicator(false)
.cachedCount(3)
.margin({
bottom: 20
})
VideoListView({
videoList: $videoList,
scrollIndex: $scrollIndex,
isBlackModule: false
})
}.width('100%')
}
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Vertical)
.scrollBarColor(Color.Gray)
.scrollBarWidth(30)
.edgeEffect(EdgeEffect.Spring)
}
.width('100%')
.height('100%')
.backgroundImage($r('app.media.main_bg'), ImageRepeat.XY)
.padding(20)
}
pageTransition() {
PageTransitionEnter({ duration: 1500,
type: RouteType.Push,
curve: Curve.Linear })
.opacity(this.opacity1)
.onEnter((type: RouteType, progress: number) => {
console.info(`${TAG} PageTransitionEnter onEnter type:${type} progress:${progress}`)
this.opacity1 = progress
})
}
}

開發(fā)詳解

1、電影數(shù)據(jù)

界面內(nèi)容需要通過數(shù)據(jù)進(jìn)行加載,目前沒有相關(guān)的電影云端,我們就先在本地mock出一些電影數(shù)據(jù),每個電影數(shù)據(jù)都應(yīng)該包含以下屬性,我們定義了一個類來表示:

VideoData.ets

export class VideoData {
id: string
name: string // 名稱
describe: string // 描述
resourceType: string // 資源類型 出品年限 類型
source:string // 來源
introduction: string // 介紹
uri: string | Resource // 資源地址
image: string | Resource // 資源圖片
actors: User[] //參演者
heat: number // 熱度
directs:User[] // 導(dǎo)演
grade:string // 評分
gradeNumber : string // 參與評分人數(shù)
}
export class User{
id: number
name: string
role: string
icon: string | Resource
}

2、構(gòu)建數(shù)據(jù)

溫馨提示:電影相關(guān)的數(shù)據(jù)是本地模擬,除了電影名稱和電影宣傳圖相關(guān),其他信息純屬虛構(gòu),如果你感興趣也可以自己構(gòu)建。

這個很簡單,就是根據(jù)VideoData所定義的數(shù)據(jù),構(gòu)建出首頁需要顯示的內(nèi)容,因為mock的數(shù)據(jù)都是自定義的,所以這里就帖部分代碼,如果你有興趣可以自行構(gòu)造,如下所示:

MockVideoData.ets

export class MockVideoData {
static getVideoList(): Array<VideoData> {
let data: Array<VideoData> = new Array()
// 電影
data.push(this.createVideoDataByImage('鐵道英雄', $r('app.media.v1')))
data.push(this.createVideoDataByImage('沙丘', $r('app.media.v2')))
data.push(this.createVideoDataByImage('那一夜我給你開過車', $r('app.media.v3')))
data.push(this.createVideoDataByImage('雷神2', $r('app.media.v4')))
data.push(this.createVideoDataByImage('大圣歸來', $r('app.media.v5')))
data.push(this.createVideoDataByImage('流浪地球', $r('app.media.v6')))
data.push(this.createVideoDataByImage('狄仁杰', $r('app.media.v7')))
data.push(this.createVideoDataByImage('獨行月球', $r('app.media.v8')))
data.push(this.createVideoDataByImage('消失的子彈', $r('app.media.v9')))
data.push(this.createVideoDataByImage('西游降魔篇', $r('app.media.v10')))
data.push(this.createVideoDataByImage('激戰(zhàn)', $r('app.media.v11')))
data.push(this.createVideoDataByImage('作妖轉(zhuǎn)', $r('app.media.v12')))
data.push(this.createVideoDataByImage('滅絕', $r('app.media.v13')))
data.push(this.createVideoDataByImage('獨行月球', $r('app.media.v14')))
data.push(this.createVideoDataByImage('超人·素人特工', $r('app.media.v15')))
data.push(this.createVideoDataByImage('戰(zhàn)狼2', $r('app.media.v16')))
data.push(this.createVideoDataByImage('四大名捕', $r('app.media.v17')))
data.push(this.createVideoDataByImage('無人區(qū)', $r('app.media.v18')))
data.push(this.createVideoDataByImage('邪不壓正', $r('app.media.v19')))
return data
}
private static createVideoDataByImage(_name, _image, uri?): VideoData {
if (typeof (uri) === 'undefined') {
uri = $rawfile('video_4.mp4')
}
return this.createVideoData(
_name,
'硬漢強(qiáng)力回歸',
'2023 / 動作 / 槍戰(zhàn)',
'愛電影',
'《邪不壓正》是由姜文編劇并執(zhí)導(dǎo),姜文、彭于晏、廖凡、周韻、許晴、澤田謙也等主演的動作喜劇電影。該片改編自張北海小說《俠隱》。講述在1937年\“七七事變\”爆發(fā)之前,北平城的“至暗時刻”,一個身負(fù)大恨、自美歸國的特工李天然,在國難之時滌蕩重重陰謀上演的一出終極復(fù)仇記。',
uri,
_image
)
}
private static createVideoData(_name, _describe, _resourceType, _source, _introduction, _uri, _image,): VideoData {
let vData: VideoData = new VideoData()
vData.id = UUIDUtils.getUUID()
vData.name = _name
vData.describe = _describe
vData.resourceType = _resourceType
vData.source = _source
vData.introduction = _introduction
vData.uri = _uri
vData.image = _image
vData.actors = []
let user1: User = new User()
user1.name = '吳京'
user1.role = '飾 吳曉曉'
user1.icon = $r('app.media.actor_02')
vData.actors.push(user1)
let user2: User = new User()
user2.name = '屈楚蕭'
user2.role = '飾 吳曉曉'
user2.icon = $r('app.media.actor_03')
vData.actors.push(user2)
let user3: User = new User()
user3.name = '吳京'
user3.role = '飾 吳曉曉'
user3.icon = $r('app.media.actor_02')
vData.actors.push(user3)
vData.heat = 89
vData.grade = '8.6'
vData.gradeNumber = '3.6萬'
vData.directs = []
for (let i = 0; i < 1; i++) {
let user: User = new User()
user.name = '戴維'
user.role = '導(dǎo)演'
user.icon = $r('app.media.actor_01')
vData.directs.push(user)
}
return vData
}


static getBannerList(): Array<VideoData> {
let data: Array<VideoData> = new Array()
// 構(gòu)建banner數(shù)據(jù),與構(gòu)建videoData類似
return data
}
}

3、banner

在Index.ets的aboutToAppear()函數(shù)中初始化數(shù)據(jù),通過MockVideoData.getBannerList()獲取到banner列表,使用Swiper滑塊組件實現(xiàn)自動輪播顯示。在Swiper容器中使用了LazyForEach懶加載的方式進(jìn)行子項的加載。簡單說明下LazyForEach懶加載機(jī)制,由于在長列表渲染中會涉及到大量的數(shù)據(jù)加載,如果處理不當(dāng)會導(dǎo)致資源占用影響性能,在ArkUI3.0針對這樣的情況提供了一種懶加載機(jī)制,它會自動根據(jù)具體的情況計算出適合渲染的數(shù)據(jù),實現(xiàn)數(shù)據(jù)的按需加載,提升UI刷新效率。

4、電影列表

在Index.ets的aboutToAppear()函數(shù)中初始化數(shù)據(jù),通過MockVideoData.getVideoList()獲取到Video列表,因為電影列表的布局在項目中其他模塊也會使用到,所以這里將電影列表抽象出一個子組件VideoListView。

VideoListView.ets

/**
* 視頻列表
*/
import { VideoData } from '../model/VideoData'
import { VideoDataSource } from '../model/VideoDataSource'
import { VideoDataUtils } from '../utils/VideoDataUtils'
import router from '@ohos.router';
const TAG: string = 'VideoListView'
@Component
export struct VideoListView {
private scrollerForGrid: Scroller = new Scroller()
@Link videoList: Array<VideoData>
@Link scrollIndex: number
@Prop isBlackModule: boolean //是否為黑色模式
build() {
// 電影列表
Grid(this.scrollerForGrid) {
LazyForEach(new VideoDataSource(this.videoList), (item: VideoData) => {
GridItem() {
Column() {
Image(item.image)
.width(200)
.height(250)
.objectFit(ImageFit.Cover)
.border({
width: this.isBlackModule ? 0 : 1,
color: '#5a66b1',
radius: 10
})
Text(item.name)
.width(200)
.height(20)
.fontColor(this.isBlackModule ? Color.Black : Color.White)
.fontSize(16)
.maxLines(1)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
.margin({
top: 10
})
Text(VideoDataUtils.getUser(item.actors))
.width(200)
.height(20)
.fontColor(this.isBlackModule ? $r('app.color.name_black') : $r('app.color.name_grey'))
.fontSize(12)
.maxLines(1)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
Text(item.describe)
.width(200)
.height(20)
.fontColor(this.isBlackModule ? $r('app.color.describe_black') : $r('app.color.describe_grey'))
.fontSize(12)
.maxLines(1)
.textOverflow({
overflow: TextOverflow.Ellipsis
})
}.width('100%')
.margin({
bottom: 10
})
.onClick(() => {
router.pushUrl({ url: 'pages/Playback',
params: {
video_data: item
} }, router.RouterMode.Single)
})
}
}, item => item.id)
}
.columnsTemplate('1fr 1fr')
.columnsGap(10)
.editMode(true)
.cachedCount(6)
.width('100%')
.height('100%')
.border({
width: 0,
color: Color.White
})
.onScrollIndex((first: number) => {
console.info(`${TAG} onScrollIndex ${first}`)
this.scrollIndex = first
if (first === 0) {
this.scrollerForGrid.scrollToIndex(0)
}
})
}
}

使用Grid實現(xiàn)電影列表,由于電影列表屬于長列表數(shù)據(jù),所以這里也使用了LazyForEach懶加載機(jī)制進(jìn)行item子項的加載,最終通過import的方式引入到Index.ets頁面中,并在布局中添加此組件。

5、滾動頁面

首頁是電影列表頁,需要加載banner和電影列表,所以整體頁面都需要可滾動,因此在banner和視頻列表容器外添加了Scroll組件。

問題1:Scroll與Grid列表嵌套時,電影列表無法顯示完整,或者無法顯示banner。

如下所示:

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

為了描述清楚這個問題,我們將界面可以觸發(fā)滑動的區(qū)域分為banner部分和VideoList部分,根據(jù)滑動的觸發(fā)區(qū)域不同,進(jìn)行如下說明:

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

1、觸發(fā)滑動區(qū)域在VideoList,當(dāng)滑動到VideoList末尾時會出現(xiàn)最后一列的item只顯示了部分,滑動區(qū)域在VideoList的時候無論怎么向上滑動都無法顯示完整;

2、在1的場景下,觸發(fā)滑動區(qū)域在banner,并向上滑動,此時可以看到,頁面整體向上移動,VideoList中缺失的item部分可以正常顯示,banner劃出界面時,VideoList可以顯示完整;

3、在2的場景下,整個界面目前都是VideoList區(qū)域,VideoList已滑動到的最后,此時向下滑動,因為觸發(fā)的區(qū)域是VideoList,所以整個VideoList向下滑動顯示,直到電影列表首項,由于整個頁面的可滑動區(qū)域都是VideoLIst,無法在觸發(fā)Scroll的滑動,所以banner無法顯示。

這個問題其實就是界面視圖高度計算和觸發(fā)滑動監(jiān)聽被消費后無法再向上層傳遞導(dǎo)致,解決這個問題有多種方式,下面我介紹其中一種。

解決方案:Scroll組件中可以添加一個Scroller滑動組件的控制器,控制器可以控制組件的滾動,比如滾動的指定高度,或者指定的index,在Grid中也可以添加一個Scroller控制器進(jìn)行列表高度控制,在Grid還可以通過onScrollIndex()事件監(jiān)聽網(wǎng)格顯示的起始位置item發(fā)生變化,返回當(dāng)前的item坐標(biāo)。當(dāng)滑動區(qū)域在VideoList時,如果item坐標(biāo)發(fā)生了變化,就更新scrollIndex,在Index.ets中監(jiān)聽scrollIndex的變化,當(dāng)scrollIndex=0時表示已經(jīng)滑動到VideoList首項,此時再向下滑動時控制Scroll的控制器,讓Scroll滑動到(0,0)位置,也就是頁面頂部,這樣就可以顯示banner;當(dāng)scrollIndex=2時,表示VideoList向上滑動到第二列,此時設(shè)置外層Scroll容器的滑動高度,讓banner劃出界面,使得VideoList可以完整顯示。

實現(xiàn)核心代碼

1、Index.ets

import { VideoDataSource } from '../model/VideoDataSource'
import { VideoData } from '../model/VideoData'
import { MockVideoData } from '../model/MockVideoData'
import router from '@ohos.router';
import { VideoListView } from '../view/VideoListView'
const TAG: string = 'Splash Index'
@Entry
@Component
struct Index {
private scrollerForScroll: Scroller = new Scroller()
@State @Watch('scrollChange') scrollIndex: number = 0

scrollChange() {
if (this.scrollIndex === 0) {
this.scrollToAnimation(0, 0)
} else if (this.scrollIndex === 2) {
this.scrollToAnimation(0, 300)
}
}
scrollToAnimation(xOffset, yOffset) {
this.scrollerForScroll.scrollTo({
xOffset: xOffset,
yOffset: yOffset,
animation: {
duration: 3000,
curve: Curve.FastOutSlowIn
}
})
}

build() {
Column() {
Scroll(this.scrollerForScroll) {
Column() {
// banner
VideoListView({
videoList: $videoList,
scrollIndex: $scrollIndex,
isBlackModule: false
})
}.width('100%')
}
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Vertical)
.scrollBarColor(Color.Gray)
.scrollBarWidth(30)
.edgeEffect(EdgeEffect.Spring)
}
.width('100%')
.height('100%')
.backgroundImage($r('app.media.main_bg'), ImageRepeat.XY)
.padding(20)
}

}

2、VideoListView.ets

/**
* 視頻列表
*/
import { VideoData } from '../model/VideoData'
import { VideoDataSource } from '../model/VideoDataSource'
import { VideoDataUtils } from '../utils/VideoDataUtils'
import router from '@ohos.router';

const TAG: string = 'VideoListView'

@Component
export struct VideoListView {
private scrollerForGrid: Scroller = new Scroller()
@Link scrollIndex: number

build() {
// 電影列表
Grid(this.scrollerForGrid) {
LazyForEach(new VideoDataSource(this.videoList), (item: VideoData) => {
GridItem() {
// item
}.width('100%')
.margin({
bottom: 10
})
.onClick(() => {
router.pushUrl({ url: 'pages/Playback',
params: {
video_data: item
} }, router.RouterMode.Single)
})
}

}, item => item.id)
}
.columnsTemplate('1fr 1fr')
.columnsGap(10)
.editMode(true)
.cachedCount(6)
.width('100%')
.height('100%')
.border({
width: 0,
color: Color.White
})
.onScrollIndex((first: number) => {
console.info(`${TAG} onScrollIndex ${first}`)
this.scrollIndex = first
if (first === 0) {
this.scrollerForGrid.scrollToIndex(0)
}
})
}
}

??想了解更多關(guān)于開源的內(nèi)容,請訪問:??

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

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

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

2023-03-29 09:32:15

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

2023-03-28 09:38:34

開發(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)視頻播放器

2021-10-21 16:00:07

鴻蒙HarmonyOS應(yīng)用

2011-07-20 16:21:20

iPhone 視頻 播放器

2015-05-21 15:25:42

VLC播放器

2015-01-22 15:44:55

Android源碼音樂播放器

2022-06-21 14:41:38

播放器適配西瓜視頻

2021-10-19 14:27: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源碼多功能播放器

2012-06-04 13:44:08

2011-06-27 11:23:21

Qt 音樂播放器
點贊
收藏

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