HarmonyOS7 ArkUI 视频列表 - 封面卡片、播放量、关注按钮实战
文章目录前言从界面倒推代码交互入口在哪里代码里的重点片段 1toggleFollow(id: number) {片段 2likesDisplay(count: number): string {使用方式完整代码收个尾前言这篇不讲空概念直接从页面代码拆。能复用的不是某个颜色值而是状态、派生数据和布局之间的组织方式。它的主线是视频列表细节落在封面卡片、播放量、关注按钮。这些细节刚好能把页面状态、用户操作和组件刷新串起来。从界面倒推代码VideoFeedPage不是一个只展示静态内容的页面。它会根据用户点击、输入、切换或计时来改变界面所以阅读代码时可以先抓三条线观察点在这个案例里的表现| 页面主题 |视频列表|| 主要文案 |教程,3天前,设计,1天前,创意,5天前|| 常用组件 |Column,ForEach,Row,Scroll,Stack,Text|| 适合练习 | 状态驱动 UI、条件样式、列表渲染、事件回调 |交互入口在哪里ArkUI 的页面刷新依赖状态变化。下面这些State字段就是页面的“开关”和“数据源”不用手动找 DOM也不用自己通知某个控件刷新。状态字段类型我会怎么理解它videosVideoItem_75fu[]列表数据源通常会被ForEach消费activeCategorystring当前选中项决定高亮和内容切换searchTextstring用户输入值参与计算或提交一个经验先把State看完再看build()页面逻辑会清楚很多。否则很容易被一长串布局代码带偏。代码里的重点片段 1toggleFollow(id: number) {这段代码承担的是页面里的“判断”或“动作”把它从布局里抽出来后build()会清爽很多。后面要加新条件也不会到处翻 UI 代码。toggleFollow(id:number){this.videosthis.videos.map(v{if(v.idid){v.isFollowed!v.isFollowedreturnv}returnv})}片段 2likesDisplay(count: number): string {这段代码承担的是页面里的“判断”或“动作”把它从布局里抽出来后build()会清爽很多。后面要加新条件也不会到处翻 UI 代码。likesDisplay(count:number):string{returncount10000?(count/10000).toFixed(1)万:count}使用方式把下面代码放进 ArkTS 页面文件中即可运行。用于正式项目时我会继续把数据模型、卡片 Builder 和页面事件拆出去页面文件只负责组合。建议练习顺序先改状态字段 - 再改交互事件 - 最后调整 UI 样式 检查重点点击是否刷新、列表 key 是否稳定、条件样式是否集中管理这份代码可以继续扩展的方向把模拟数据替换成接口数据抽出复用卡片组件增加空状态和异常状态补充深色模式或主题色适配对输入类场景增加校验提示完整代码// VideoFeedPage - 视频列表封面卡片、播放量、关注按钮interfaceVideoItem_75fu{id:numbertitle:stringauthor:stringavatar:stringcoverColor:ResourceColor coverEmoji:stringviews:stringlikes:numberduration:stringcategory:stringisFollowed:booleanpublishTime:string}interfaceCategoryTab_i98l{key:stringlabel:string}EntryComponentstruct VideoFeedPage{Statevideos:VideoItem_75fu[][{id:1,title:HarmonyOS ArkTS 零基础入门教程 | 手把手教你开发第一个鸿蒙应用,author:鸿蒙技术派,avatar:,coverColor:#1E3A5F,coverEmoji:,views:18.6万,likes:3420,duration:23:45,category:教程,isFollowed:false,publishTime:3天前},{id:2,title:UI设计神技 | 10个让界面瞬间高级的小技巧99%的人不知道,author:UI大魔王,avatar:,coverColor:#3D1A78,coverEmoji:✨,views:45.2万,likes:8900,duration:12:30,category:设计,isFollowed:true,publishTime:1天前},{id:3,title:用代码画出星空 | p5.js 粒子系统实战演示,author:代码艺术家,avatar:,coverColor:#0A1628,coverEmoji:,views:7.3万,likes:1580,duration:18:22,category:创意,isFollowed:false,publishTime:5天前},{id:4,title:2024 最值得学的前端框架大盘点别再迷茫了,author:前端老炮儿,avatar:,coverColor:#1A3A1A,coverEmoji:,views:32.1万,likes:6200,duration:15:10,category:教程,isFollowed:true,publishTime:2天前},{id:5,title:从0到1搭建完整的后台管理系统 | Vue3 TypeScript 实战,author:全栈工程师小明,avatar:⚡,coverColor:#3A1A1A,coverEmoji:️,views:9.8万,likes:2100,duration:45:00,category:实战,isFollowed:false,publishTime:1周前},{id:6,title:设计师眼中的好代码 | 前端与UI如何高效协作,author:协作达人,avatar:,coverColor:#1A2A3A,coverEmoji:,views:5.6万,likes:890,duration:20:15,category:经验,isFollowed:false,publishTime:4天前}]StateactiveCategory:string推荐StatesearchText:stringprivatecategories:CategoryTab_i98l[][{key:推荐,label: 推荐},{key:教程,label: 教程},{key:设计,label: 设计},{key:实战,label:⚡ 实战},{key:创意,label:✨ 创意}]toggleFollow(id:number){this.videosthis.videos.map(v{if(v.idid){v.isFollowed!v.isFollowedreturnv}returnv})}likesDisplay(count:number):string{returncount10000?(count/10000).toFixed(1)万:count}getfilteredVideos():VideoItem_75fu[]{if(this.activeCategory推荐)returnthis.videosreturnthis.videos.filter(vv.categorythis.activeCategory)}BuildervideoCard(video:VideoItem_75fu){Column({space:0}){// 视频封面Stack({alignContent:Alignment.Bottom}){// 封面背景Column(){Text(video.coverEmoji).fontSize(60)}.width(100%).height(180).backgroundColor(video.coverColor).justifyContent(FlexAlign.Center).borderRadius({topLeft:12,topRight:12})// 时长标签Row(){Blank()Text(video.duration).fontSize(12).fontColor(#FFFFFF).padding({left:8,right:8,top:3,bottom:3}).backgroundColor(#000000AA).borderRadius(6)}.width(100%).padding({right:10,bottom:10})// 播放按钮圆形半透明Circle({width:50,height:50}).fill(#00000060).position({x:50%,y:50%}).translate({x:-25,y:-25-10})Text(▶).fontSize(18).fontColor(#FFFFFF).position({x:50%,y:50%}).translate({x:-8,y:-8-10})}.width(100%)// 视频信息Row({space:10}){// 作者头像Text(video.avatar).fontSize(24).width(40).height(40).textAlign(TextAlign.Center).backgroundColor(#F3F4F6).borderRadius(20)Column({space:4}){Text(video.title).fontSize(14).fontColor(#111827).maxLines(2).textOverflow({overflow:TextOverflow.Ellipsis}).lineHeight(20)Row({space:6}){Text(video.author).fontSize(12).fontColor(#6B7280)Text(·).fontSize(12).fontColor(#D1D5DB)Text(video.views次播放).fontSize(12).fontColor(#9CA3AF)Text(·).fontSize(12).fontColor(#D1D5DB)Text(video.publishTime).fontSize(12).fontColor(#9CA3AF)}}.layoutWeight(1).alignItems(HorizontalAlign.Start)}.width(100%).padding({left:12,right:12,top:10,bottom:6})// 底部互动栏Row({space:0}){Row({space:6}){Text().fontSize(14)Text(this.likesDisplay(video.likes)).fontSize(13).fontColor(#6B7280)}.layoutWeight(1)Row({space:6}){Text().fontSize(14)Text(评论).fontSize(13).fontColor(#6B7280)}.layoutWeight(1)Row({space:6}){Text(↗️).fontSize(14)Text(分享).fontSize(13).fontColor(#6B7280)}.layoutWeight(1)// 关注按钮Text(video.isFollowed?✓ 已关注: 关注).fontSize(13).fontColor(video.isFollowed?#6B7280:#FFFFFF).padding({left:14,right:14,top:6,bottom:6}).backgroundColor(video.isFollowed?#F3F4F6:#3B82F6).borderRadius(16).onClick(()this.toggleFollow(video.id))}.width(100%).padding({left:12,right:12,top:4,bottom:12})}.width(100%).backgroundColor(#FFFFFF).borderRadius(12).shadow({radius:6,color:#00000010,offsetX:0,offsetY:2}).clip(true)}build(){Column(){// 顶部搜索栏Row({space:10}){Row({space:8}){Text().fontSize(16).fontColor(#9CA3AF)Text(搜索视频、创作者...).fontSize(14).fontColor(#9CA3AF)Blank()}.layoutWeight(1).height(40).padding({left:14,right:14}).backgroundColor(#F3F4F6).borderRadius(20)Text().fontSize(22)Text().fontSize(22)}.width(100%).padding({left:16,right:16,top:50,bottom:12}).backgroundColor(#FFFFFF)// 分类 TabScroll(){Row({space:8}){ForEach(this.categories,(cat:CategoryTab_i98l){Text(cat.label).fontSize(14).fontColor(this.activeCategorycat.key?#FFFFFF:#374151).padding({left:16,right:16,top:8,bottom:8}).backgroundColor(this.activeCategorycat.key?#3B82F6:#F3F4F6).borderRadius(20).onClick((){this.activeCategorycat.key})})}.padding({left:16,right:16,bottom:10,top:4})}.scrollable(ScrollDirection.Horizontal).backgroundColor(#FFFFFF).border({width:{bottom:0.5},color:#F3F4F6})// 视频列表Scroll(){Column({space:14}){ForEach(this.filteredVideos,(video:VideoItem_75fu){this.videoCard(video)})}.padding({left:16,right:16,top:14,bottom:30})}.layoutWeight(1).backgroundColor(#F9FAFB)}.width(100%).height(100%).backgroundColor(#F9FAFB)}}收个尾这个案例可以当成一个小模板先把页面会变的东西放进状态再用函数或 Builder 处理重复逻辑最后让布局只关心展示。写 HarmonyOS7 页面时这个顺序通常比一上来堆 UI 更稳。