当前位置: 首页 > news >正文

鸿蒙简易版影视APP案例实战

目录

1. 案例效果

2. 资源初始化和资源文件

2.1. string.json (en_US)

2.2. string.json (zh_CN)

2.3. constants

3. 视频列表

3.1. 顶部导航

3.1.1. TobBar 组件

3.1.2. TopBar 数据源

3.2. 全部分类内容页面

3.2.1. 全部分类组件

3.2.2. 轮播图组件

3.2.3. 图片列表组件

3.2.4. 图片视图

3.2.5. 图片视图模型

3.2.6. 图片模型

3.2.7. 图片类型和数据源

3.3. 电影分类页面

3.3.1. 电影分类组件

3.3.2. 电影分类视图

3.4. 其他分类页面

3.4.1. 电视剧组件

3.4.2. 综艺组件

3.4.3. 直播组件

3.4.4. 游戏组件

4. 轮播实现

4.1. 首页

4.2. 修改 TabBar 组件

4.3. 修改 Banner 组件

4.4. 联调预览

5. 视频滑动播放

5.1. 在 Banner 组件上添加路由

5.2. 在图片视图上添加路由

5.3. 视频播放首页

5.4. 视图模型

5.4.1. 视频模型

5.4.2. 视频数据源

5.4.3. 视频视图模型

5.5. 播放组件

5.5.1. 播放视图

5.5.2. 导航视图

5.5.3. 互动视图

5.5.4. 描述视图


1. 案例效果

2. 资源初始化和资源文件

2.1. string.json (en_US)

{"string": [{"name": "module_desc","value": "module description"},{"name": "EntryAbility_desc","value": "description"},{"name": "EntryAbility_label","value": "Use of Swiper"},{"name": "recently","value": "Recent Plays"},{"name": "photo","value": "camera"},{"name": "more","value": "more >"},{"name": "movie_classic","value": "Selected Films"},{"name": "lately","value": "latest"},{"name": "like","value": "like"},{"name": "comment","value": "comment"},{"name": "share","value": "share"},{"name": "movie","value": "movie"},{"name": "movie_description_1","value": "@HarmonyOS Official website"},{"name": "movie_description_2","value": "#HarmonyOS Huawei Developer Conference"},{"name": "TV","value": "TV"},{"name": "game","value": "Game"},{"name": "live","value": "Live"},{"name": "entertainment","value": "Entertainment"}]
}

2.2. string.json (zh_CN)

{"string": [{"name": "module_desc","value": "module description"},{"name": "EntryAbility_desc","value": "description"},{"name": "EntryAbility_label","value": "Swiper的使用"},{"name": "recently","value": "最近播放"},{"name": "photo","value": "相机"},{"name": "more","value": "更多 >"},{"name": "movie_classic","value": "电影精选"},{"name": "lately","value": "最新"},{"name": "like","value": "点赞"},{"name": "comment","value": "评论"},{"name": "share","value": "转发"},{"name": "movie","value": "视频"},{"name": "movie_description_1","value": "@HarmonyOS 官网"},{"name": "movie_description_2","value": "#HarmonyOS HDC大会"},{"name": "TV","value": "电视剧"},{"name": "game","value": "游戏"},{"name": "live","value": "直播"},{"name": "entertainment","value": "综艺"}]
}

2.3. constants

  • CommonConstants
// ets/common/constants/CommonConstants.etsexport class CommonConstants {static readonly DURATION_PAGE = 50static readonly DURATION_ADS = 200static readonly HEIGHT_HEAD = 40static readonly HEIGHT_CAROUSEL_TITLE = 90static readonly FONT_SIZE_DESCRIPTION = 12static readonly FONT_SIZE_PHOTO_NAME = 14static readonly FONT_SIZE_SORT_TITLE = 16static readonly FONT_SIZE_UNCHECKED = 18static readonly FONT_SIZE_TITLE = 20static readonly FONT_SIZE_CHECKED = 24static readonly FONT_SIZE_PAGE_CONTENT = 28static readonly FONT_WEIGHT_LIGHT = 400static readonly FONT_WEIGHT_NORMAL = 500static readonly FONT_WEIGHT_BOLD = 700static readonly LAYOUT_WEIGHT = 1static readonly BORDER_RADIUS = 12static readonly LINE_HEIGHT_MORE = 19static readonly LINE_HEIGHT_NAVIGATION = 28static readonly SPACE_TOP_BAR = 16static readonly SPACE_NAVIGATION = 8static readonly WIDTH_HEAD_BORDER = 2static readonly WIDTH_HEAD = 40static readonly RADIUS_HEAD = 20static readonly SWIPER_TIME = 1500static readonly MARGIN_PLAY_PAGE = 10static readonly BOTTOM_TEXT = 4static readonly TOP_ADS = 12static readonly LEFT_POSITION = '3%'static readonly ADS_LEFT = 12static readonly TOP_NAME = 8static readonly TOP_DESCRIPTION = 4static readonly TOP_IMAGE_VOTE = 20static readonly TOP_HEAD = 40static readonly FULL_WIDTH = '100%'static readonly FULL_HEIGHT = '100%'static readonly WIDTH_PLAY = '95%'static readonly PAGE_WIDTH = '94.4%'static readonly WIDTH_SORT_NAME = '62.2%'static readonly WIDTH_SORT = '92%'static readonly WIDTH_MOVIE_SORT = '90%'static readonly WIDTH_PICTURE = '72%'static readonly HEIGHT_BANNER = '27%'static readonly WIDTH_VOTE = '8.9%'static readonly WIDTH_BACK_ICON = '6.7%'static readonly MARGIN_TOP_SORT = '3.2%'static readonly MARGIN_BOTTOM_SORT = '1.7%'static readonly MARGIN_BOTTOM_GRID = '4.2%'static readonly WIDTH_VIDEO = '26.2%'static readonly TWO_COLUMNS = '1fr 1fr'static readonly TWO_ROWS = '1fr 1fr'static readonly THREE_COLUMNS = '1fr 1fr 1fr'static readonly THREE_ROWS = '1fr 1fr 1fr'static readonly GAP_COLUMNS = '2.2%'static readonly HEIGHT_GRID = '45%'static readonly HEIGHT_DESCRIPTION = '12.3%'static readonly TOP_BAR_HEIGHT = '7.2%'static readonly HEIGHT_COMMENT = '4.1%'static readonly HEIGHT_BACK_ICON = '3.1%'static readonly OFFSET_COMMENT_X = '-5%'static readonly OFFSET_COMMENT_Y = '10%'static readonly OFFSET_DESCRIPTION_Y = '45%'static readonly START_POSITION = '0%'static readonly PLAY_PAGE = 'pages/PageVideo'static readonly HOME_PAGE = 'pages/SwiperIndex'
}

3. 视频列表

3.1. 顶部导航

3.1.1. TobBar 组件
// ets/view/common/TopBar.etsimport { TopBarItem } from '../../viewmodel/TopBarItem'
import { initializeOnStartup } from '../../viewmodel/TopBarViewModel'
import { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct TopBar {@Prop index: number = 0private tabArray: Array<TopBarItem> = initializeOnStartup()build() {Row({ space: CommonConstants.SPACE_TOP_BAR }) {ForEach(this.tabArray,(item: TopBarItem) => {Text(item.name).fontSize(this.index === item.id ? CommonConstants.FONT_SIZE_CHECKED : CommonConstants.FONT_SIZE_UNCHECKED).fontColor(Color.Black).textAlign(TextAlign.Center).fontWeight(this.index === item.id ? FontWeight.Bold : FontWeight.Regular)}, (item: TopBarItem) => JSON.stringify(item))}.margin({ left: CommonConstants.ADS_LEFT }).width(CommonConstants.FULL_WIDTH).height(CommonConstants.TOP_BAR_HEIGHT)}
}
3.1.2. TopBar 数据源
// ets/viewmodel/TopBarViewModel.etsimport { TopBarItem } from './TopBarItem'
import { TOP_BAR_DATA } from '../common/constants/TopBarConstants'export function initializeOnStartup(): Array<TopBarItem> {let tabDataArray: Array<TopBarItem> = []TOP_BAR_DATA.forEach((item: TopBarItem) => {tabDataArray.push(new TopBarItem(item.id, item.name))})return tabDataArray
}
// ets/common/constants/TopBarConstants.etsimport { TopBarItem } from '../../viewmodel/TopBarItem'export const TOP_BAR_DATA: TopBarItem[] = [new TopBarItem(0, '全部'),new TopBarItem(1, '电影'),new TopBarItem(2, '电视剧'),new TopBarItem(3, '综艺'),new TopBarItem(4, '直播'),new TopBarItem(5, '游戏')
]
// ets/viewmodel/TopBarItem.etsexport class TopBarItem {id: numbername: stringconstructor(id: number, name: string) {this.id = idthis.name = name}
}

3.2. 全部分类内容页面

3.2.1. 全部分类组件
// ets/view/tabcontent/PageAll.etsimport { Banner } from '../common/Banner'
import { PictureSort } from '../all/PictureSort'
import { CommonConstants } from '../../common/constants/CommonConstants'
import { PictureType } from '../../common/constants/PictureConstants'@Preview
@Component
export struct PageAll {build() {Scroll() {Column() {Banner()PictureSort({ initType: PictureType.RECENTLY })PictureSort({ initType: PictureType.PHOTO })}.width(CommonConstants.FULL_WIDTH)}}
}
3.2.2. 轮播图组件
// ets/view/common/Banner.etsimport { CommonConstants } from "../../common/constants/CommonConstants"@Component
export struct Banner {build() {Column() {Text('swiper')}.width(CommonConstants.PAGE_WIDTH).height(CommonConstants.HEIGHT_BANNER)}
}
3.2.3. 图片列表组件
// ets/view/all/PictureSort.etsimport { PictureItem } from '../../viewmodel/PictureItem'
import { initializePictures } from '../../viewmodel/PictureViewModel'
import { PictureView } from '../common/PictureView'
import { PictureType } from '../../common/constants/PictureConstants'
import { CommonConstants } from '../../common/constants/CommonConstants'@Extend(Text)
function textStyle(fontSize: number, fontWeight: number) {.fontSize(fontSize).fontWeight(fontWeight).fontColor($r('app.color.font_black'))
}@Component
export struct PictureSort {@State photos: Array<PictureItem> = []@State private sortName: Resource = $r('app.string.recently')private initType: string = ''aboutToAppear() {if (PictureType.RECENTLY === this.initType) {this.sortName = $r('app.string.recently')this.photos = initializePictures(PictureType.RECENTLY)} else {this.sortName = $r('app.string.photo');this.photos = initializePictures(PictureType.PHOTO)}}build() {Column() {Row() {Text(this.sortName).width(CommonConstants.WIDTH_SORT_NAME).textStyle(CommonConstants.FONT_SIZE_SORT_TITLE, CommonConstants.FONT_WEIGHT_NORMAL)Text($r('app.string.more')).layoutWeight(CommonConstants.LAYOUT_WEIGHT).textAlign(TextAlign.End).textStyle(CommonConstants.FONT_SIZE_PHOTO_NAME, CommonConstants.FONT_WEIGHT_LIGHT).lineHeight(CommonConstants.LINE_HEIGHT_MORE).opacity($r('app.float.opacity_light'))}.width(CommonConstants.WIDTH_SORT).margin({ top: CommonConstants.MARGIN_TOP_SORT, bottom: CommonConstants.MARGIN_BOTTOM_SORT })Grid() {ForEach(this.photos, (item: PictureItem) => {GridItem() {PictureView({ photos: item })}}, (item: PictureItem) => JSON.stringify(item))}.columnsTemplate(CommonConstants.TWO_COLUMNS).rowsTemplate(CommonConstants.TWO_ROWS).columnsGap(CommonConstants.GAP_COLUMNS).rowsGap(CommonConstants.GAP_COLUMNS).width(CommonConstants.PAGE_WIDTH).height(CommonConstants.HEIGHT_GRID).margin({ bottom: CommonConstants.MARGIN_BOTTOM_GRID })}}
}
3.2.4. 图片视图
// ets/view/common/PictureView.etsimport { PictureItem } from '../../viewmodel/PictureItem'
import { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct PictureView {private photos: PictureItem = new PictureItem()build() {Column() {Image(this.photos.image).borderRadius(CommonConstants.BORDER_RADIUS).height(CommonConstants.WIDTH_PICTURE)Text(this.photos.name).width(CommonConstants.PAGE_WIDTH).fontSize(CommonConstants.FONT_SIZE_PHOTO_NAME).fontWeight(CommonConstants.FONT_WEIGHT_NORMAL).margin({ top: CommonConstants.TOP_NAME })Text(this.photos.description).width(CommonConstants.PAGE_WIDTH).fontSize(CommonConstants.FONT_SIZE_DESCRIPTION).fontWeight(CommonConstants.FONT_WEIGHT_LIGHT).opacity($r('app.float.opacity_light')).margin({ top: CommonConstants.TOP_DESCRIPTION, bottom: CommonConstants.BOTTOM_TEXT })}.height(CommonConstants.FULL_HEIGHT)}
}
3.2.5. 图片视图模型
// ets/viewmodel/PictureViewModel.etsimport { PictureItem } from './PictureItem'
import { PICTURE_RECENTLY, PICTURE_PHOTO, PICTURE_LATEST, PICTURE_BANNER } from '../common/constants/PictureConstants'
import { PictureType } from '../common/constants/PictureConstants'export function initializePictures(initType: string): Array<PictureItem> {let imageDataArray: Array<PictureItem> = []switch (initType) {case PictureType.BANNER:PICTURE_BANNER.forEach((item: PictureItem) => {imageDataArray.push(item)})breakcase PictureType.RECENTLY:PICTURE_RECENTLY.forEach((item: PictureItem) => {imageDataArray.push(item)})breakcase PictureType.PHOTO:PICTURE_PHOTO.forEach((item: PictureItem) => {imageDataArray.push(item)})breakcase PictureType.LATEST:PICTURE_LATEST.forEach((item: PictureItem) => {imageDataArray.push(item)})breakdefault:break}return imageDataArray
}
3.2.6. 图片模型
export class PictureItem {id: string = ''name: string = ''description: string = ''image: Resource = $r('app.media.image1')
}
3.2.7. 图片类型和数据源
// ets/common/constants/PictureConstants.etsimport { PictureItem } from '../../viewmodel/PictureItem'export const PICTURE_BANNER: PictureItem[] = [{ id: '1', name: '怒海', description: '怒海波涛', image: $r('app.media.image1') },{ id: '2', name: '大山深处', description: '大山深处感人的亲情之歌', image: $r('app.media.image2') },{ id: '3', name: '荒漠', description: '荒漠的亲情之歌', image: $r('app.media.image3') }
]export const PICTURE_RECENTLY: PictureItem[] = [{ id: '1', name: '背影', description: '感人的亲情之歌', image: $r('app.media.recently1') },{ id: '2', name: '废墟之上', description: '勇闯无人之境', image: $r('app.media.recently2') },{ id: '3', name: '无根之人', description: '悬疑国产力作', image: $r('app.media.recently3') },{ id: '4', name: '摩天轮', description: '每个人心中都有一个童话', image: $r('app.media.recently4') }
]export const PICTURE_PHOTO: PictureItem[] = [{ id: '1', name: '蓝·静', description: '用放大镜看世界', image: $r('app.media.photo1') },{ id: '2', name: '花', description: '每个人心中都有一个童话', image: $r('app.media.photo2') },{ id: '3', name: '无根之人', description: '悬疑国产力作', image: $r('app.media.recently3') },{ id: '4', name: '摩天轮', description: '每个人心中都有一个童话', image: $r('app.media.recently4') }
]export const PICTURE_LATEST: PictureItem[] = [{ id: '1', name: '潮·设计大会', description: '国际设计大师分...', image: $r('app.media.movie1') },{ id: '2', name: '食客', description: '味蕾爆炸', image: $r('app.media.movie2') },{ id: '3', name: '绿野仙踪', description: '热带雨林的故事', image: $r('app.media.image3') },{ id: '4', name: '塔', description: '2021最期待的电...', image: $r('app.media.movie4') },{ id: '5', name: '微缩世界', description: '用放大镜看世界', image: $r('app.media.movie5') },{ id: '6', name: '非常规接触', description: '少年的奇妙之旅', image: $r('app.media.movie6') },{ id: '7', name: '绿野仙踪', description: '热带雨林的故事', image: $r('app.media.movie7') },{ id: '8', name: '塔', description: '用放大镜看世界', image: $r('app.media.movie8') },{ id: '9', name: '食客', description: '热带雨林的故事', image: $r('app.media.movie9') }
]export enum PictureType {RECENTLY = 'recently',PHOTO = 'photo',LATEST = 'latest',BANNER = 'banner'
}

3.3. 电影分类页面

3.3.1. 电影分类组件
// ets/view/tabcontent/PageMovie.etsimport { Banner } from '../common/Banner'
import { MovieSort } from '../movie/MovieSort'
import { CommonConstants } from '../../common/constants/CommonConstants'@Preview
@Component
export struct PageMovie {build() {Scroll() {Column() {Banner()MovieSort()}.width(CommonConstants.FULL_WIDTH)}.scrollable(ScrollDirection.Vertical).scrollBar(BarState.Off)}
}
3.3.2. 电影分类视图
// ets/view/movie/MovieSort.etsimport { PictureItem } from '../../viewmodel/PictureItem'
import { initializePictures } from '../../viewmodel/PictureViewModel'
import { PictureType } from '../../common/constants/PictureConstants'
import { PictureView } from '../common/PictureView'
import { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct MovieSort {@State photos: Array<PictureItem> = initializePictures(PictureType.LATEST)build() {Column() {Text($r('app.string.lately')).width(CommonConstants.WIDTH_SORT).margin({ top: CommonConstants.MARGIN_TOP_SORT, bottom: CommonConstants.MARGIN_BOTTOM_SORT }).fontSize(CommonConstants.FONT_SIZE_SORT_TITLE).fontWeight(CommonConstants.FONT_WEIGHT_NORMAL).fontColor($r('app.color.font_black'))Grid() {ForEach(this.photos, (item: PictureItem) => {GridItem() {PictureView({ photos: item })}}, (item: PictureItem) => JSON.stringify(item))}.columnsTemplate(CommonConstants.THREE_COLUMNS).rowsTemplate(CommonConstants.THREE_ROWS).columnsGap(CommonConstants.GAP_COLUMNS).rowsGap(CommonConstants.GAP_COLUMNS).width(CommonConstants.PAGE_WIDTH).height(CommonConstants.WIDTH_MOVIE_SORT).margin({ bottom: CommonConstants.MARGIN_BOTTOM_GRID })}}
}

3.4. 其他分类页面

3.4.1. 电视剧组件
// ets/view/tabcontent/PageTV.etsimport { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct PageTV {build() {Column() {Text($r('app.string.TV')).height(CommonConstants.FULL_HEIGHT).fontSize(CommonConstants.FONT_SIZE_PAGE_CONTENT).fontWeight(CommonConstants.FONT_WEIGHT_LIGHT)}.width(CommonConstants.FULL_WIDTH).height(CommonConstants.FULL_HEIGHT)}
}
3.4.2. 综艺组件
// ets/view/tabcontent/PageEntertainment.etsimport { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct PageEntertainment {build() {Column() {Text($r('app.string.entertainment')).height(CommonConstants.FULL_HEIGHT).fontSize(CommonConstants.FONT_SIZE_PAGE_CONTENT).fontWeight(CommonConstants.FONT_WEIGHT_LIGHT)}.width(CommonConstants.FULL_WIDTH).height(CommonConstants.FULL_HEIGHT)}
}
3.4.3. 直播组件
// ets/view/tabcontent/PageLive.etsimport { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct PageLive {build() {Column() {Text($r('app.string.live')).height(CommonConstants.FULL_HEIGHT).fontSize(CommonConstants.FONT_SIZE_PAGE_CONTENT).fontWeight(CommonConstants.FONT_WEIGHT_LIGHT)}.width(CommonConstants.FULL_WIDTH).height(CommonConstants.FULL_HEIGHT)}
}
3.4.4. 游戏组件
// ets/view/tabcontent/PageContent.etsimport { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct PageGame {build() {Column() {Text($r('app.string.game')).height(CommonConstants.FULL_HEIGHT).fontSize(CommonConstants.FONT_SIZE_PAGE_CONTENT).fontWeight(CommonConstants.FONT_WEIGHT_LIGHT)}.width(CommonConstants.FULL_WIDTH).height(CommonConstants.FULL_HEIGHT)}
}

4. 轮播实现

4.1. 首页

// ets/pages/SwiperIndex.etsimport { CommonConstants } from '../common/constants/CommonConstants'
import { TopBar } from '../view/common/TobBar'
import { PageAll } from '../view/tabcontent/PageAll'
import { PageEntertainment } from '../view/tabcontent/PageEntertainment'
import { PageGame } from '../view/tabcontent/PageGame'
import { PageLive } from '../view/tabcontent/PageLive'
import { PageMovie } from '../view/tabcontent/PageMovie'
import { PageTV } from '../view/tabcontent/PageTV'@Entry
@Component
struct SwiperIndex {@State index: number = 0build() {Flex({direction: FlexDirection.Column,alignItems: ItemAlign.Start}) {TopBar({ index: $index })Swiper() {PageAll()PageMovie()PageTV()PageEntertainment()PageLive()PageGame()}.index(this.index).indicator(false).loop(false).duration(CommonConstants.DURATION_PAGE).onChange((index: number) => {this.index = index})}.backgroundColor($r('app.color.start_window_background'))}
}

4.2. 修改 TabBar 组件

import { TopBarItem } from '../../viewmodel/TopBarItem'
import { initializeOnStartup } from '../../viewmodel/TopBarViewModel'
import { CommonConstants } from '../../common/constants/CommonConstants'@Preview
@Component
export struct TopBar {// @Prop index: number = 0// 1. @Prop 改为 @Link@Link index: numberprivate tabArray: Array<TopBarItem> = initializeOnStartup()build() {Row({ space: CommonConstants.SPACE_TOP_BAR }) {ForEach(this.tabArray,(item: TopBarItem) => {Text(item.name).fontSize(this.index === item.id ? CommonConstants.FONT_SIZE_CHECKED : CommonConstants.FONT_SIZE_UNCHECKED).fontColor(Color.Black).textAlign(TextAlign.Center).fontWeight(this.index === item.id ? FontWeight.Bold : FontWeight.Regular)// 2. 绑定事件,修改index,实现swiper切换.onClick(() => {this.index = item.id})}, (item: TopBarItem) => JSON.stringify(item))}.margin({ left: CommonConstants.ADS_LEFT }).width(CommonConstants.FULL_WIDTH).height(CommonConstants.TOP_BAR_HEIGHT)}
}

4.3. 修改 Banner 组件

// ets/view/common/Banner.etsimport { PictureItem } from '../../viewmodel/PictureItem'
import { PictureType } from '../../common/constants/PictureConstants'
import { initializePictures, startPlay, stopPlay } from '../../viewmodel/PictureViewModel'
import { CommonConstants } from '../../common/constants/CommonConstants'@Extend(Text)
function textStyle(fontSize: number, fontWeight: number) {.fontSize(fontSize).fontColor($r('app.color.start_window_background')).fontWeight(fontWeight)
}@Component
export struct Banner {@State index: number = 0private imageArray: Array<PictureItem> = []private swiperController: SwiperController = new SwiperController()private dotIndicator: DotIndicator = new DotIndicator()aboutToAppear() {this.dotIndicator.selectedColor($r('app.color.start_window_background'));this.imageArray = initializePictures(PictureType.BANNER);startPlay(this.swiperController);}aboutToDisappear() {stopPlay()}build() {Swiper(this.swiperController) {ForEach(this.imageArray, (item: PictureItem) => {Stack({ alignContent: Alignment.TopStart }) {Image(item.image).objectFit(ImageFit.Fill).height(CommonConstants.FULL_HEIGHT).width(CommonConstants.FULL_WIDTH).borderRadius(CommonConstants.BORDER_RADIUS).align(Alignment.Center)Column() {Text($r('app.string.movie_classic')).textStyle(CommonConstants.FONT_SIZE_DESCRIPTION, CommonConstants.FONT_WEIGHT_LIGHT).opacity($r('app.float.opacity_deep')).margin({ bottom: CommonConstants.BOTTOM_TEXT })Text(item.name).textStyle(CommonConstants.FONT_SIZE_TITLE, CommonConstants.FONT_WEIGHT_BOLD)}.alignItems(HorizontalAlign.Start).height(CommonConstants.HEIGHT_CAROUSEL_TITLE).margin({ top: CommonConstants.TOP_ADS, left: CommonConstants.ADS_LEFT })}.height(CommonConstants.FULL_HEIGHT).width(CommonConstants.FULL_WIDTH)}, (item: PictureItem) => JSON.stringify(item))}.width(CommonConstants.PAGE_WIDTH).height(CommonConstants.HEIGHT_BANNER).index(this.index).indicator(this.dotIndicator).duration(CommonConstants.DURATION_ADS)}
}
// ets/viewmoel/PictureViewModel.etsimport { PictureItem } from './PictureItem'
import { PICTURE_RECENTLY, PICTURE_PHOTO, PICTURE_LATEST, PICTURE_BANNER } from '../common/constants/PictureConstants'
import { PictureType } from '../common/constants/PictureConstants'
import { CommonConstants } from '../common/constants/CommonConstants'export function initializePictures(initType: string): Array<PictureItem> {let imageDataArray: Array<PictureItem> = []switch (initType) {case PictureType.BANNER:PICTURE_BANNER.forEach((item: PictureItem) => {imageDataArray.push(item)})breakcase PictureType.RECENTLY:PICTURE_RECENTLY.forEach((item: PictureItem) => {imageDataArray.push(item)})breakcase PictureType.PHOTO:PICTURE_PHOTO.forEach((item: PictureItem) => {imageDataArray.push(item)})breakcase PictureType.LATEST:PICTURE_LATEST.forEach((item: PictureItem) => {imageDataArray.push(item)})breakdefault:break}return imageDataArray
}// 添加swiper调度任务
let timerIds: number[] = []export function startPlay(swiperController: SwiperController): void {let timerId = setInterval(() => {swiperController.showNext()}, CommonConstants.SWIPER_TIME)timerIds.push(timerId)
}export function stopPlay(): void {timerIds.forEach((item: number) => {clearTimeout(item)})
}

4.4. 联调预览

去掉 PageAll、PageMovie、TabBar 等组件的 @Preview 装饰器,打开 SwiperIndex 开始预览。

5. 视频滑动播放

5.1. 在 Banner 组件上添加路由

// 1. 导入路由模块
import { router } from '@kit.ArkUI'import { PictureItem } from '../../viewmodel/PictureItem'
import { PictureType } from '../../common/constants/PictureConstants'
import { initializePictures, startPlay, stopPlay } from '../../viewmodel/PictureViewModel'
import { CommonConstants } from '../../common/constants/CommonConstants'@Extend(Text)
function textStyle(fontSize: number, fontWeight: number) {.fontSize(fontSize).fontColor($r('app.color.start_window_background')).fontWeight(fontWeight)
}@Component
export struct Banner {@State index: number = 0private imageArray: Array<PictureItem> = []private swiperController: SwiperController = new SwiperController()private dotIndicator: DotIndicator = new DotIndicator()aboutToAppear() {this.dotIndicator.selectedColor($r('app.color.start_window_background'));this.imageArray = initializePictures(PictureType.BANNER);startPlay(this.swiperController);}aboutToDisappear() {stopPlay()}build() {Swiper(this.swiperController) {ForEach(this.imageArray, (item: PictureItem) => {Stack({ alignContent: Alignment.TopStart }) {Image(item.image).objectFit(ImageFit.Fill).height(CommonConstants.FULL_HEIGHT).width(CommonConstants.FULL_WIDTH).borderRadius(CommonConstants.BORDER_RADIUS).align(Alignment.Center)// 2.添加路由导航.onClick(() => {router.pushUrl({ url: CommonConstants.PLAY_PAGE })})Column() {Text($r('app.string.movie_classic')).textStyle(CommonConstants.FONT_SIZE_DESCRIPTION, CommonConstants.FONT_WEIGHT_LIGHT).opacity($r('app.float.opacity_deep')).margin({ bottom: CommonConstants.BOTTOM_TEXT })Text(item.name).textStyle(CommonConstants.FONT_SIZE_TITLE, CommonConstants.FONT_WEIGHT_BOLD)}.alignItems(HorizontalAlign.Start).height(CommonConstants.HEIGHT_CAROUSEL_TITLE).margin({ top: CommonConstants.TOP_ADS, left: CommonConstants.ADS_LEFT })}.height(CommonConstants.FULL_HEIGHT).width(CommonConstants.FULL_WIDTH)}, (item: PictureItem) => JSON.stringify(item))}.width(CommonConstants.PAGE_WIDTH).height(CommonConstants.HEIGHT_BANNER).index(this.index).indicator(this.dotIndicator).duration(CommonConstants.DURATION_ADS)}
}

5.2. 在图片视图上添加路由

// ets/view/common/PictureView.etsimport { router } from '@kit.ArkUI'
import { PictureItem } from '../../viewmodel/PictureItem'
import { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct PictureView {private photos: PictureItem = new PictureItem()build() {Column() {Image(this.photos.image).borderRadius(CommonConstants.BORDER_RADIUS).height(CommonConstants.WIDTH_PICTURE).onClick(() => {router.pushUrl({ url: CommonConstants.PLAY_PAGE })})Text(this.photos.name).width(CommonConstants.PAGE_WIDTH).fontSize(CommonConstants.FONT_SIZE_PHOTO_NAME).fontWeight(CommonConstants.FONT_WEIGHT_NORMAL).margin({ top: CommonConstants.TOP_NAME })Text(this.photos.description).width(CommonConstants.PAGE_WIDTH).fontSize(CommonConstants.FONT_SIZE_DESCRIPTION).fontWeight(CommonConstants.FONT_WEIGHT_LIGHT).opacity($r('app.float.opacity_light')).margin({ top: CommonConstants.TOP_DESCRIPTION, bottom: CommonConstants.BOTTOM_TEXT })}.height(CommonConstants.FULL_HEIGHT)}
}

5.3. 视频播放首页

// ets/pages/PageVideo.etsimport { VideoItem } from '../viewmodel/VideoItem'
import { initializeOnStartup } from '../viewmodel/VideoViewModel'
import { PlayView } from '../view/play/PlayView'
import { CommonConstants } from '../common/constants/CommonConstants'@Entry
@Component
struct PageVideo {@State videoArray: Array<VideoItem> = initializeOnStartup()@State index: number = 0@State pageShow: boolean = falsebuild() {Column() {Swiper() {ForEach(this.videoArray, (item: VideoItem, index: number | undefined) => {PlayView({index: $index,pageShow: $pageShow,item: item,barPosition: index})}, (item: VideoItem) => JSON.stringify(item))}.width(CommonConstants.FULL_WIDTH).height(CommonConstants.FULL_HEIGHT).indicator(false).loop(false).vertical(true).onChange((index: number) => {this.index = index})}}onPageShow(): void {this.pageShow = true}onPageHide(): void {this.pageShow = false}
}

5.4. 视图模型

5.4.1. 视频模型
// ets/viewmodel/VideoItem.ets@Observed
export class VideoItem {id: string = ''src: Resource = $rawfile('video1.mp4')likesCount: number = 0isLikes: boolean = falsecommentCount: number = 102shareTimes: number = 666
}
5.4.2. 视频数据源
// ets/common/constants/VideoConstants.etsimport { VideoItem } from '../../viewmodel/VideoItem'export const VIDEO_DATA: VideoItem[] = [{id: '1',src: $rawfile('video1.mp4'),likesCount: 0,isLikes: false,commentCount: 102,shareTimes: 666},{id: '2',src: $rawfile('video2.mp4'),likesCount: 8654,isLikes: true,commentCount: 0,shareTimes: 0}
]export enum PlayState {STOP = 0,START = 1,PAUSE = 2
}
5.4.3. 视频视图模型
// ets/viewmodel/VideoViewModel.etsimport { VideoItem } from './VideoItem'
import { VIDEO_DATA } from '../common/constants/VideoConstants'export function initializeOnStartup(): Array<VideoItem> {let videoDataArray: Array<VideoItem> = []VIDEO_DATA.forEach((item: VideoItem) => {videoDataArray.push(item)})return videoDataArray
}

5.5. 播放组件

5.5.1. 播放视图
// ets/view/play/PlayView.etsimport { VideoItem } from '../../viewmodel/VideoItem'
import { CommonConstants } from '../../common/constants/CommonConstants'
import { PlayState } from '../../common/constants/VideoConstants'
import { NavigationView } from './NavigationView'
import { CommentView } from './CommentView'
import { DescriptionView } from './DescriptionView'@Component
export struct PlayView {private isShow: boolean = false@Link @Watch('needPageShow') index: number@Link @Watch('needPageShow') pageShow: boolean@State item: VideoItem = new VideoItem()private barPosition: number = 0@State private playState: number = PlayState.STOPprivate videoController: VideoController = new VideoController()build() {Stack({ alignContent: Alignment.End }) {Video({src: this.item.src,controller: this.videoController}).controls(false).autoPlay(this.playState === PlayState.START ? true : false).objectFit(ImageFit.Fill).loop(true).height(CommonConstants.WIDTH_VIDEO).width(CommonConstants.FULL_WIDTH).onClick(() => {if (this.playState === PlayState.START) {this.playState = PlayState.PAUSEthis.videoController.pause()} else if (this.playState === PlayState.PAUSE) {this.playState = PlayState.STARTthis.videoController.start()}})NavigationView()CommentView({ item: this.item })DescriptionView()}.backgroundColor(Color.Black).width(CommonConstants.FULL_WIDTH).height(CommonConstants.FULL_HEIGHT)}onPageSwiperShow(): void {if (this.playState != PlayState.START) {this.playState = PlayState.STARTthis.videoController.start()}}onPageSwiperHide(): void {if (this.playState != PlayState.STOP) {this.playState = PlayState.STOPthis.videoController.stop()}}needPageShow(): void {if (this.pageShow === true) {if (this.barPosition === this.index) {this.isShow = truethis.onPageSwiperShow()} else {if (this.isShow === true) {this.isShow = falsethis.onPageSwiperHide()}}} else {this.onPageSwiperHide()}}
}
5.5.2. 导航视图
// ets/view/play/NavigationView.etsimport { CommonConstants } from '../../common/constants/CommonConstants'@Component
export struct NavigationView {build() {Navigator({ target: CommonConstants.HOME_PAGE, type: NavigationType.Back }) {Row({ space: CommonConstants.SPACE_NAVIGATION }) {Image($r('app.media.ic_back')).width(CommonConstants.WIDTH_BACK_ICON).height(CommonConstants.HEIGHT_BACK_ICON).objectFit(ImageFit.Contain)Text($r('app.string.movie')).fontSize(CommonConstants.FONT_SIZE_TITLE).fontWeight(CommonConstants.FONT_WEIGHT_BOLD).fontColor($r('app.color.start_window_background')).textAlign(TextAlign.Center).margin(CommonConstants.MARGIN_PLAY_PAGE).lineHeight(CommonConstants.LINE_HEIGHT_NAVIGATION)}}.position({ x: CommonConstants.LEFT_POSITION, y: CommonConstants.START_POSITION })}
}
5.5.3. 互动视图
// ets/view/play/CommentView.etsimport { VideoItem } from '../../viewmodel/VideoItem';
import { CommonConstants } from '../../common/constants/CommonConstants'@Extend(Text) function textStyle(fontSize: number, fonWeight: number) {.fontSize(fontSize).fontWeight(fonWeight).fontColor($r('app.color.start_window_background')).textAlign(TextAlign.Center)
}@Component
export struct CommentView {@ObjectLink item: VideoItembuild() {Column() {Image($r('app.media.head')).width(CommonConstants.WIDTH_HEAD).height(CommonConstants.HEIGHT_HEAD).margin({ top: CommonConstants.TOP_HEAD }).objectFit(ImageFit.Contain).border({width: CommonConstants.WIDTH_HEAD_BORDER,color: Color.White,radius: CommonConstants.RADIUS_HEAD})Image(this.item.isLikes ? $r('app.media.vote1') : $r('app.media.vote0')).width(CommonConstants.WIDTH_VOTE).height(CommonConstants.HEIGHT_COMMENT).onClick(() => {if (this.item.isLikes) {this.item.likesCount--} else {this.item.likesCount++}this.item.isLikes = !this.item.isLikes;}).margin({ top: CommonConstants.TOP_IMAGE_VOTE })Text(this.item.likesCount === 0 ? $r('app.string.like') : (this.item.likesCount.toString())).textStyle(CommonConstants.FONT_SIZE_DESCRIPTION, CommonConstants.FONT_WEIGHT_LIGHT)Image($r('app.media.comment')).width(CommonConstants.WIDTH_VOTE).height(CommonConstants.HEIGHT_COMMENT).margin({ top: CommonConstants.TOP_IMAGE_VOTE })Text(this.item.commentCount === 0 ? $r('app.string.comment') : (this.item.commentCount.toString())).textStyle(CommonConstants.FONT_SIZE_DESCRIPTION, CommonConstants.FONT_WEIGHT_LIGHT)Image($r('app.media.share')).width(CommonConstants.WIDTH_VOTE).height(CommonConstants.HEIGHT_COMMENT).margin({ top: CommonConstants.TOP_IMAGE_VOTE })Text(this.item.shareTimes === 0 ? $r('app.string.share') : (this.item.shareTimes.toString())).textStyle(CommonConstants.FONT_SIZE_DESCRIPTION, CommonConstants.FONT_WEIGHT_LIGHT)}.offset({ x: CommonConstants.OFFSET_COMMENT_X, y: CommonConstants.OFFSET_COMMENT_Y })}
}
5.5.4. 描述视图
// ets/view/play/DescriptionView.etsimport { CommonConstants } from '../../common/constants/CommonConstants'@Extend(Text) function textStyle(fontSize: number, fonWeight: number) {.fontSize(fontSize).fontWeight(fonWeight).fontColor($r('app.color.start_window_background')).textAlign(TextAlign.Center).margin(CommonConstants.MARGIN_PLAY_PAGE)
}@Component
export struct DescriptionView {build() {Column() {Text($r('app.string.movie_description_1')).textStyle(CommonConstants.FONT_SIZE_SORT_TITLE, CommonConstants.FONT_WEIGHT_NORMAL)Text($r('app.string.movie_description_2')).textStyle(CommonConstants.FONT_SIZE_PHOTO_NAME, CommonConstants.FONT_WEIGHT_LIGHT).opacity($r('app.float.opacity_deep'))}.height(CommonConstants.HEIGHT_DESCRIPTION).width(CommonConstants.WIDTH_PLAY).alignItems(HorizontalAlign.Start).offset({ y: CommonConstants.OFFSET_DESCRIPTION_Y })}
}

相关文章:

  • 代码随想录算法训练营第60期第五十六天打卡
  • Linux 环境下高效视频切帧的实用指南
  • AXURE安装+汉化-Windows
  • CSS 平铺+自动换行效果
  • ADC接口电路设计详解
  • Vue 树状结构控件
  • 黑马Java面试笔记之 集合篇(算法复杂度+ArrayList+)
  • 免费开源Umi-OCR,离线使用,批量精准!
  • Trae CN IDE自动生成注释功能测试与效率提升全解析
  • 点击启动「高效模式」:大腾智能 CAD 重构研发设计生产力
  • “packageManager“: “pnpm@9.6.0“ 配置如何正确启动项目?
  • 《Pytorch深度学习实践》ch5-Logistic回归
  • Ubuntu系统安装与配置NTP时间同步服务
  • 邢台山峰特种橡胶制品有限公司专题报道
  • 实战商品订单秒杀设计实现
  • 蜜獾算法(HBA,Honey Badger Algorithm)
  • LangChain核心之Runnable接口底层实现
  • matlab实现掺杂光纤放大器的模拟
  • Termux下如何使用MATLAB
  • GCC内存占用统计使用指南
  • 吉安哪里做网站/电商运营培训课程
  • 招商加盟网/重庆放心seo整站优化
  • 企业邮箱是哪个/360排名优化工具
  • 一台ip做两个网站/线上营销推广的公司
  • 深圳市顺建建设工程有限公司网站/搜索引擎优化简历
  • 博望哪里做网站/舆情网站直接打开的软件