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

鸿蒙简易版影视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 })}
}

http://www.xdnf.cn/news/10712.html

相关文章:

  • Attention Is All You Need (Transformer) 以及Transformer pytorch实现
  • 软件评测师 综合测试 真题笔记
  • 基于Python学习《Head First设计模式》第五章 单件模式
  • MySQL 灾备方案介绍
  • 模拟电子技术 第一章<半导体基础>
  • OpenCV 滑动条调整图像对比度和亮度
  • 嵌入式SDK技术EasyRTC音视频实时通话助力即时通信社交/教育等多场景创新应用
  • 攻防世界-unseping
  • GPR全波形反演中三种初始模型建立方法详解
  • 【嵌入式(2)深入剖析嵌入式开发:从基础到实战】
  • vue3学习
  • 大数据-277 Spark MLib - 基础介绍 机器学习算法 Gradient Boosting GBDT算法原理 高效实现
  • 简单transformer运用
  • 学习NuxtLink标签
  • dvwa6——Insecure CAPTCHA
  • Lovable + Cursor:零基础搭建专业应用的秘密武器
  • Windows下WSL(Ubuntu)安装1Panel
  • ASR技术(自动语音识别)深度解析
  • Eigen实现非线性最小二乘拟合 + Gauss-Newton算法
  • RabbitMQ如何保证消息可靠性
  • python中可以对数组使用的所有方法
  • 工作自动化——工作自动提炼--智能编程——仙盟创梦IDE
  • B站缓存视频数据m4s转mp4
  • 从零开始,搭建一个基于 Django 的 Web 项目
  • django入门-orm数据库操作
  • unity UI Canvas“高”性能写法
  • 如何轻松地将数据从 iPhone传输到iPhone 16
  • 【JSON-to-Video】设置背景视频片断
  • 【OCCT+ImGUI系列】011-Poly-Poly_Triangle三角形面片
  • GIC v3 v4 虚拟化架构