当前位置: 首页 > 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 })}
}

http://www.lqws.cn/news/111277.html

相关文章:

  • nav2笔记-250603
  • cacti导出的1分钟监控数据csv文件读取并按5分钟求平均值,然后计算95计费值,假设31天的月份
  • 你的台式机PCIe插槽到底是几条lane
  • day18 leetcode-hot100-36(二叉树1)
  • YOLO训练及数据采集注意事项
  • 力扣HOT100之多维动态规划:5. 最长回文子串
  • 软件评测师 综合测试 真题笔记
  • Spring @Autowired自动装配的实现机制
  • Silky-CTF: 0x02靶场
  • ADI硬件笔试面试题型解析上
  • spring boot应答500问题跟踪
  • 帝可得- 人员管理
  • CppCon 2014 学习:CONVERGENT EVOLUTION
  • Redis线程模型
  • 解决CSDN等网站访问不了的问题
  • Cursor使用最佳实践总结
  • 枫之谷Artale端午节大当机----后端技术的巨大风险
  • 涂装协作机器人:重新定义涂装工艺的智能化未来
  • Matlab回归预测大合集又更新啦!新增2种高斯过程回归预测模型,已更新41个模型!性价比拉满!
  • QGIS 矢量数据属性表中文乱码解决方案:4 步修复编码匹配问题
  • 高性能MCU的MPU与Cache优化详解
  • 降本增效的新引擎:GEO如何提升企业营销ROI
  • SKUA-GOCAD入门教程-第八节 线的创建与编辑2
  • 板凳-------Mysql cookbook学习 (九--3)
  • 大模型的分词器——算法及示例
  • 实战商品订单秒杀设计实现
  • 飞牛fnNAS存储模式RAID 5数据恢复
  • 简单transformer运用
  • 第七章 7.Warm Up Packet Tracer and IOS Basic (CCNA)
  • 中英混合编码解码全解析