154.[HarmonyOS NEXT 实战案例十一 :List系列] 自定义内容列表 - 进阶篇
2025-06-30 22:28:35
103次阅读
0个评论
[HarmonyOS NEXT 实战案例十一 :List系列] 自定义内容列表 - 进阶篇
项目已开源,开源地址: https://gitcode.com/nutpi/HarmonyosNextCaseStudyTutorial , 欢迎fork & star
效果演示
1. 概述
在基础篇中,我们学习了如何创建一个基本的自定义内容列表,展示不同类型的社交媒体内容。在这个进阶篇中,我们将深入探讨更高级的功能和优化技巧,使自定义内容列表更加完善和专业。
1.1 进阶功能概览
- 内容交互与动画效果
- 高级布局与自适应设计
- 内容过滤与个性化推荐
- 高级状态管理
- 手势交互与操作
- 内容分享与社交功能
2. 高级内容交互与动画
2.1 内容展开与折叠
对于长文本内容,我们可以实现展开与折叠功能,提升用户体验:
@State isExpanded: boolean = false
@State showExpandButton: boolean = false
@Builder
AdvancedTextContent(content: string, maxLines: number = 3) {
Column() {
Text(content)
.fontSize(16)
.margin({ top: 12, bottom: this.showExpandButton ? 4 : 12 })
.maxLines(this.isExpanded ? undefined : maxLines)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.onlineRender(true) // 提高渲染性能
.onTouch((event) => {
if (event.type === TouchType.Down) {
// 检测文本是否需要展开按钮
// 实际应用中可以通过测量文本高度来判断
this.showExpandButton = content.length > 100
}
return true
})
if (this.showExpandButton) {
Text(this.isExpanded ? '收起' : '展开')
.fontSize(14)
.fontColor('#007AFF')
.margin({ bottom: 12 })
.onClick(() => {
animateTo({ duration: 300, curve: Curve.EaseOut }, () => {
this.isExpanded = !this.isExpanded
})
})
}
}
}
2.2 图片浏览与缩放
为图片内容添加点击预览和缩放功能:
@State currentPreviewImage: Resource | null = null
@State showImagePreview: boolean = false
@Builder
AdvancedImageContent(images: Resource[]) {
// 基本图片布局代码...
// 为每个图片添加点击事件
.onClick(() => {
this.currentPreviewImage = image
this.showImagePreview = true
})
// 图片预览弹窗
if (this.showImagePreview && this.currentPreviewImage) {
Column() {
// 关闭按钮
Image($r('app.media.close'))
.width(32)
.height(32)
.position({ top: 40, right: 20 })
.onClick(() => {
this.showImagePreview = false
})
// 图片预览,支持手势缩放
Gesture(
GestureGroup(GestureMode.Exclusive,
PinchGesture({ fingers: 2 })
.onActionUpdate((event) => {
// 实现缩放逻辑
}),
PanGesture()
.onActionUpdate((event) => {
// 实现平移逻辑
})
)
) {
Image(this.currentPreviewImage)
.width('100%')
.height('80%')
.objectFit(ImageFit.Contain)
}
}
.width('100%')
.height('100%')
.backgroundColor('#000000E6')
.position({ x: 0, y: 0 })
.zIndex(999)
}
}
2.3 视频内容的高级处理
实现视频内容的自动播放、暂停和进度控制:
@State isPlaying: boolean = false
@State currentProgress: number = 0
@State videoDuration: number = 60 // 假设视频时长为60秒
@Builder
AdvancedVideoContent(video: Resource) {
Stack({ alignContent: Alignment.Center }) {
// 实际应用中应使用Video组件
Image(video)
.width('100%')
.height(240)
.objectFit(ImageFit.Cover)
.borderRadius(8)
.opacity(this.isPlaying ? 0.8 : 1)
if (!this.isPlaying) {
// 播放按钮
Image($r('app.media.01'))
.width(60)
.height(60)
.onClick(() => {
this.isPlaying = true
// 开始播放视频的逻辑
this.startVideoPlayback()
})
} else {
// 暂停按钮
Image($r('app.media.pause'))
.width(60)
.height(60)
.opacity(0.7)
.onClick(() => {
this.isPlaying = false
// 暂停视频的逻辑
this.pauseVideoPlayback()
})
}
// 视频进度条
if (this.isPlaying) {
Column() {
Slider({
value: this.currentProgress,
min: 0,
max: this.videoDuration,
step: 1,
style: SliderStyle.OutSet
})
.width('90%')
.onChange((value) => {
this.currentProgress = value
// 更新视频播放进度的逻辑
this.updateVideoProgress(value)
})
Text(`${Math.floor(this.currentProgress / 60)}:${Math.floor(this.currentProgress % 60).toString().padStart(2, '0')} / ${Math.floor(this.videoDuration / 60)}:${Math.floor(this.videoDuration % 60).toString().padStart(2, '0')}`)
.fontSize(12)
.fontColor('#FFFFFF')
}
.width('100%')
.position({ y: '85%' })
}
}
.margin({ top: 12, bottom: 12 })
// 模拟视频播放进度更新
// 实际应用中应该使用Video组件的事件
startVideoPlayback() {
// 模拟视频播放进度更新
this.videoTimer = setInterval(() => {
if (this.currentProgress < this.videoDuration) {
this.currentProgress++
} else {
this.isPlaying = false
clearInterval(this.videoTimer)
this.currentProgress = 0
}
}, 1000)
}
pauseVideoPlayback() {
clearInterval(this.videoTimer)
}
updateVideoProgress(value: number) {
// 实际应用中应该调用Video组件的seek方法
}
}
3. 高级布局与自适应设计
3.1 响应式布局
实现根据屏幕尺寸自动调整的响应式布局:
@State screenWidth: number = 0
aboutToAppear() {
// 获取屏幕宽度
this.screenWidth = px2vp(window.getWindowWidth())
// 监听屏幕旋转事件
window.on('resize', () => {
this.screenWidth = px2vp(window.getWindowWidth())
})
}
// 根据屏幕宽度调整布局
getImageLayout() {
if (this.screenWidth < 600) {
// 窄屏布局
return {
columns: 2,
imageHeight: 120
}
} else if (this.screenWidth < 840) {
// 中等屏幕布局
return {
columns: 3,
imageHeight: 160
}
} else {
// 宽屏布局
return {
columns: 4,
imageHeight: 200
}
}
}
3.2 瀑布流布局
实现瀑布流布局,使内容展示更加丰富多样:
@Builder
WaterfallLayout(posts: Post[]) {
WaterFlow({ footer: this.ListFooter }) {
ForEach(posts, (post: Post) => {
FlowItem() {
// 内容卡片
Column() {
// 用户信息
Row() {
Image(post.user.avatar)
.width(32)
.height(32)
.borderRadius(16)
Text(post.user.name)
.fontSize(14)
.margin({ left: 8 })
}
.width('100%')
.margin({ bottom: 8 })
// 内容展示
if (post.contentType === 'image' && post.media) {
Image(post.media[0])
.width('100%')
.aspectRatio(post.id % 3 === 0 ? 1 : (post.id % 3 === 1 ? 4/3 : 3/4))
.objectFit(ImageFit.Cover)
.borderRadius(8)
}
// 文本内容
Text(post.content)
.fontSize(14)
.margin({ top: 8, bottom: 8 })
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// 互动信息
Row() {
Row() {
Image($r('app.media.heart_outline'))
.width(16)
.height(16)
Text(post.likes.toString())
.fontSize(12)
.margin({ left: 4 })
}
Row() {
Image($r('app.media.note_icon'))
.width(16)
.height(16)
Text(post.comments.toString())
.fontSize(12)
.margin({ left: 4 })
}
.margin({ left: 16 })
}
.width('100%')
}
.width('100%')
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
}
.width('100%')
})
}
.columnsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.layoutWeight(1)
.padding(8)
}
@Builder
ListFooter() {
Column() {
if (this.isLoading) {
LoadingProgress()
.width(24)
.height(24)
Text('加载中...')
.fontSize(14)
.margin({ top: 8 })
} else {
Text('没有更多内容了')
.fontSize(14)
.fontColor('#999999')
}
}
.width('100%')
.padding({ top: 16, bottom: 16 })
.justifyContent(FlexAlign.Center)
}
4. 内容过滤与个性化推荐
4.1 内容分类与标签
实现内容分类和标签过滤功能:
// 内容分类
enum ContentCategory {
All = '全部',
Recommended = '推荐',
Following = '关注',
Trending = '热门',
Nearby = '附近'
}
// 内容标签
interface ContentTag {
id: number;
name: string;
}
// 为Post添加分类和标签
interface Post {
// 原有属性...
category: ContentCategory;
tags: ContentTag[];
}
@State currentCategory: ContentCategory = ContentCategory.All
@State selectedTags: number[] = []
// 过滤内容
getFilteredPosts(): Post[] {
return this.posts.filter(post => {
// 分类过滤
if (this.currentCategory !== ContentCategory.All && post.category !== this.currentCategory) {
return false
}
// 标签过滤
if (this.selectedTags.length > 0) {
const hasSelectedTag = post.tags.some(tag => this.selectedTags.includes(tag.id))
if (!hasSelectedTag) {
return false
}
}
return true
})
}
// 分类选择器UI
@Builder
CategorySelector() {
Row() {
ForEach(Object.values(ContentCategory), (category: string) => {
Text(category)
.fontSize(16)
.fontWeight(this.currentCategory === category ? FontWeight.Bold : FontWeight.Normal)
.fontColor(this.currentCategory === category ? '#FF5722' : '#333333')
.padding({ left: 12, right: 12, top: 8, bottom: 8 })
.backgroundColor(this.currentCategory === category ? '#FFF3F0' : 'transparent')
.borderRadius(16)
.margin({ right: 8 })
.onClick(() => {
this.currentCategory = category as ContentCategory
})
})
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.scrollable(ScrollDirection.Horizontal)
}
4.2 内容搜索功能
实现内容搜索功能:
@State searchText: string = ''
@State isSearching: boolean = false
@State searchResults: Post[] = []
// 搜索内容
searchPosts() {
if (this.searchText.trim() === '') {
this.searchResults = []
return
}
// 简单的文本匹配搜索
this.searchResults = this.posts.filter(post => {
return post.content.toLowerCase().includes(this.searchText.toLowerCase()) ||
post.user.name.toLowerCase().includes(this.searchText.toLowerCase())
})
}
// 搜索框UI
@Builder
SearchBar() {
Row() {
if (!this.isSearching) {
Image($r('app.media.search'))
.width(24)
.height(24)
.margin({ right: 8 })
} else {
Image($r('app.media.back'))
.width(24)
.height(24)
.margin({ right: 8 })
.onClick(() => {
this.isSearching = false
this.searchText = ''
this.searchResults = []
})
}
TextInput({ placeholder: '搜索内容', text: this.searchText })
.width('80%')
.height(40)
.backgroundColor('#F5F5F5')
.borderRadius(20)
.padding({ left: 16, right: 16 })
.onChange((value) => {
this.searchText = value
this.searchPosts()
})
.onSubmit(() => {
this.searchPosts()
})
.onClick(() => {
this.isSearching = true
})
if (this.searchText !== '') {
Image($r('app.media.clear'))
.width(24)
.height(24)
.margin({ left: 8 })
.onClick(() => {
this.searchText = ''
this.searchResults = []
})
}
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
}
5. 高级状态管理
5.1 使用AppStorage进行状态管理
// 在应用启动时初始化
AppStorage.SetOrCreate('likedPosts', new Set<number>())
AppStorage.SetOrCreate('savedPosts', new Set<number>())
// 在组件中使用
@StorageLink('likedPosts') likedPosts: Set<number> = new Set<number>()
@StorageLink('savedPosts') savedPosts: Set<number> = new Set<number>()
// 点赞操作
toggleLike(id: number) {
if (this.likedPosts.has(id)) {
this.likedPosts.delete(id)
} else {
this.likedPosts.add(id)
}
// 更新UI状态
this.posts = this.posts.map(post => {
if (post.id === id) {
post.isLiked = this.likedPosts.has(id)
post.likes = post.isLiked ? post.likes + 1 : post.likes - 1
}
return post
})
}
// 保存操作
toggleSave(id: number) {
if (this.savedPosts.has(id)) {
this.savedPosts.delete(id)
} else {
this.savedPosts.add(id)
}
}
5.2 使用PersistentStorage持久化状态
// 定义持久化存储
PersistentStorage.PersistProp<Set<number>>('likedPosts', new Set<number>())
PersistentStorage.PersistProp<Set<number>>('savedPosts', new Set<number>())
// 在组件中使用
@StorageProp('likedPosts') likedPosts: Set<number> = new Set<number>()
@StorageProp('savedPosts') savedPosts: Set<number> = new Set<number>()
6. 手势交互与操作
6.1 滑动操作菜单
实现滑动显示操作菜单的功能:
@State swipedPostId: number | null = null
@Builder
SwipeablePostItem(post: Post) {
Stack() {
// 操作菜单背景
Row() {
Button() {
Image($r('app.media.share'))
.width(24)
.height(24)
.fillColor('#FFFFFF')
}
.width(80)
.height('100%')
.backgroundColor('#4CAF50')
.onClick(() => {
// 分享操作
this.sharePost(post)
})
Button() {
Image($r('app.media.delete'))
.width(24)
.height(24)
.fillColor('#FFFFFF')
}
.width(80)
.height('100%')
.backgroundColor('#F44336')
.onClick(() => {
// 删除操作
this.deletePost(post.id)
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.End)
// 内容卡片
Column() {
// 帖子内容...
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.translate({ x: this.swipedPostId === post.id ? -160 : 0 })
.gesture(
PanGesture({ direction: PanDirection.Horizontal })
.onActionStart(() => {
// 开始滑动时记录当前帖子ID
this.swipedPostId = post.id
})
.onActionUpdate((event) => {
// 限制最大滑动距离
if (event.offsetX < -160) {
event.offsetX = -160
} else if (event.offsetX > 0) {
event.offsetX = 0
}
})
.onActionEnd((event) => {
// 根据滑动距离决定是否显示操作菜单
if (event.offsetX < -80) {
animateTo({ duration: 300 }, () => {
// 显示完整操作菜单
this.swipedPostId = post.id
})
} else {
animateTo({ duration: 300 }, () => {
// 恢复原位
this.swipedPostId = null
})
}
})
)
}
.width('100%')
.height(post.contentType === 'text' ? 'auto' : 'auto')
.clip(true)
}
6.2 双击点赞
实现双击点赞功能:
@Builder
DoubleTapLikeContent(post: Post) {
Stack() {
// 内容展示
// ...
// 点赞动画
if (this.doubleTapLikePostId === post.id) {
Image($r('app.media.heart_filled'))
.width(80)
.height(80)
.fillColor('#FF5722')
.opacity(this.likeAnimationOpacity)
.scale({ x: this.likeAnimationScale, y: this.likeAnimationScale })
.onAppear(() => {
// 播放点赞动画
animateTo(
{ duration: 600, curve: Curve.Ease },
() => {
this.likeAnimationScale = 1.2
this.likeAnimationOpacity = 0
}
)
})
.onDisAppear(() => {
// 重置动画状态
this.likeAnimationScale = 0.5
this.likeAnimationOpacity = 1
this.doubleTapLikePostId = null
})
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.gesture(
TapGesture({ count: 2 })
.onAction(() => {
// 双击点赞
if (!post.isLiked) {
this.toggleLike(post.id)
}
// 显示点赞动画
this.doubleTapLikePostId = post.id
})
)
}
7. 内容分享与社交功能
7.1 分享菜单
实现内容分享菜单:
@State showShareMenu: boolean = false
@State sharePostId: number | null = null
// 分享帖子
sharePost(post: Post) {
this.sharePostId = post.id
this.showShareMenu = true
}
@Builder
ShareMenu() {
if (this.showShareMenu && this.sharePostId) {
Column() {
// 分享标题
Text('分享到')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ top: 16, bottom: 16 })
// 分享选项
Grid() {
// 微信
GridItem() {
Column() {
Image($r('app.media.wechat'))
.width(48)
.height(48)
.borderRadius(24)
Text('微信')
.fontSize(14)
.margin({ top: 8 })
}
.onClick(() => {
// 分享到微信
this.shareToWechat()
})
}
// 朋友圈
GridItem() {
Column() {
Image($r('app.media.moments'))
.width(48)
.height(48)
.borderRadius(24)
Text('朋友圈')
.fontSize(14)
.margin({ top: 8 })
}
.onClick(() => {
// 分享到朋友圈
this.shareToMoments()
})
}
// 微博
GridItem() {
Column() {
Image($r('app.media.weibo'))
.width(48)
.height(48)
.borderRadius(24)
Text('微博')
.fontSize(14)
.margin({ top: 8 })
}
.onClick(() => {
// 分享到微博
this.shareToWeibo()
})
}
// 更多选项...
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr')
.columnsGap(16)
.width('100%')
.padding({ left: 16, right: 16 })
// 取消按钮
Button('取消')
.width('90%')
.height(44)
.margin({ top: 24, bottom: 16 })
.backgroundColor('#F5F5F5')
.fontColor('#333333')
.onClick(() => {
this.showShareMenu = false
})
}
.width('100%')
.padding({ top: 16, bottom: 16 })
.backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.position({ x: 0, y: '70%' })
}
}
7.2 评论功能
实现评论功能:
interface Comment {
id: number;
postId: number;
user: User;
content: string;
time: string;
likes: number;
isLiked: boolean;
replies?: Comment[];
}
@State showComments: boolean = false
@State currentPostId: number | null = null
@State commentText: string = ''
@State comments: Comment[] = [
// 初始评论数据
]
// 打开评论面板
openComments(postId: number) {
this.currentPostId = postId
this.showComments = true
}
// 添加评论
addComment() {
if (this.commentText.trim() === '' || !this.currentPostId) {
return
}
// 创建新评论
const newComment: Comment = {
id: this.comments.length + 1,
postId: this.currentPostId,
user: {
name: '我',
avatar: $r('app.media.avatar_me')
},
content: this.commentText,
time: '刚刚',
likes: 0,
isLiked: false
}
// 添加到评论列表
this.comments.unshift(newComment)
// 更新帖子评论数
this.posts = this.posts.map(post => {
if (post.id === this.currentPostId) {
post.comments++
}
return post
})
// 清空输入框
this.commentText = ''
}
@Builder
CommentPanel() {
if (this.showComments && this.currentPostId) {
Column() {
// 评论标题
Row() {
Text('评论')
.fontSize(18)
.fontWeight(FontWeight.Medium)
Blank()
Image($r('app.media.close'))
.width(24)
.height(24)
.onClick(() => {
this.showComments = false
})
}
.width('100%')
.padding({ left: 16, right: 16, top: 16, bottom: 16 })
// 评论列表
List() {
ForEach(this.getPostComments(this.currentPostId), (comment: Comment) => {
ListItem() {
Row() {
// 用户头像
Image(comment.user.avatar)
.width(40)
.height(40)
.borderRadius(20)
// 评论内容
Column() {
Text(comment.user.name)
.fontSize(14)
.fontWeight(FontWeight.Medium)
Text(comment.content)
.fontSize(16)
.margin({ top: 4, bottom: 4 })
Row() {
Text(comment.time)
.fontSize(12)
.fontColor('#999999')
Text('回复')
.fontSize(12)
.fontColor('#666666')
.margin({ left: 16 })
Blank()
Row() {
Image(comment.isLiked ? $r('app.media.heart_filled') : $r('app.media.heart_outline'))
.width(16)
.height(16)
.fillColor(comment.isLiked ? '#FF5722' : '#666666')
Text(comment.likes.toString())
.fontSize(12)
.fontColor('#666666')
.margin({ left: 4 })
}
.onClick(() => {
// 点赞评论
this.toggleCommentLike(comment.id)
})
}
.width('100%')
}
.alignItems(HorizontalAlign.Start)
.margin({ left: 12 })
.layoutWeight(1)
}
.width('100%')
.padding({ top: 12, bottom: 12 })
}
})
}
.width('100%')
.layoutWeight(1)
.padding({ left: 16, right: 16 })
// 评论输入框
Row() {
TextInput({ placeholder: '添加评论...', text: this.commentText })
.width('80%')
.height(40)
.backgroundColor('#F5F5F5')
.borderRadius(20)
.padding({ left: 16, right: 16 })
.onChange((value) => {
this.commentText = value
})
Button() {
Text('发送')
.fontSize(14)
.fontColor(this.commentText.trim() !== '' ? '#FFFFFF' : '#AAAAAA')
}
.width(60)
.height(40)
.margin({ left: 8 })
.borderRadius(20)
.backgroundColor(this.commentText.trim() !== '' ? '#007AFF' : '#F5F5F5')
.onClick(() => {
this.addComment()
})
}
.width('100%')
.padding({ left: 16, right: 16, top: 8, bottom: 8 })
.backgroundColor('#FFFFFF')
.borderColor('#E5E5E5')
.borderWidth({ top: 1 })
}
.width('100%')
.height('80%')
.backgroundColor('#FFFFFF')
.borderRadius({ topLeft: 16, topRight: 16 })
.position({ x: 0, y: '20%' })
}
}
// 获取指定帖子的评论
getPostComments(postId: number): Comment[] {
return this.comments.filter(comment => comment.postId === postId)
}
// 点赞评论
toggleCommentLike(commentId: number) {
this.comments = this.comments.map(comment => {
if (comment.id === commentId) {
comment.isLiked = !comment.isLiked
comment.likes = comment.isLiked ? comment.likes + 1 : comment.likes - 1
}
return comment
})
}
8. 常见问题与解决方案
8.1 列表性能优化
问题:当列表项较多且复杂时,可能导致滚动卡顿和内存占用过高。
解决方案:
- 使用
LazyForEach
替代ForEach
,实现虚拟列表 - 设置合理的
cachedCount
值,控制缓存的列表项数量 - 使用
onlineRender
属性提高渲染性能 - 减少列表项的嵌套层级和复杂度
8.2 图片加载优化
问题:大量图片加载可能导致内存占用过高和UI卡顿。
解决方案:
- 使用
syncLoad(false)
异步加载图片 - 实现图片懒加载,只在图片进入视口时才加载
- 使用适当的图片尺寸和压缩比例
- 实现图片缓存机制,避免重复加载
8.3 手势冲突处理
问题:多个手势可能发生冲突,导致交互体验不佳。
解决方案:
- 使用
GestureGroup
和GestureMode.Exclusive
设置手势优先级 - 合理设置手势的触发条件和范围
- 使用
event.stopPropagation()
阻止事件传播
9. 总结与扩展
在本教程中,我们深入探讨了如何实现一个功能丰富、交互良好的自定义内容列表,包括高级内容交互与动画、响应式布局、内容过滤与搜索、高级状态管理、手势交互以及社交功能等方面。
00
- 0回答
- 4粉丝
- 0关注
相关话题
- 153.[HarmonyOS NEXT 实战案例十一 :List系列] 自定义内容列表 - 基础篇
- 148.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表进阶篇
- 156.[HarmonyOS NEXT 实战案例十二 :List系列] 聊天消息列表 - 进阶篇
- 146.[HarmonyOS NEXT 实战案例七 :List系列] 可选择列表进阶篇
- 150.[HarmonyOS NEXT 实战案例十一:List系列] 下拉刷新和上拉加载更多列表组件实战:打造高效新闻应用 进阶篇
- 134.[HarmonyOS NEXT 实战案例六:List系列] 垂直列表组件实战:打造高效联系人列表 进阶篇
- 142.[HarmonyOS NEXT 实战案例九:List系列] 分组列表组件实战:打造分类设置菜单 进阶篇
- 138.[HarmonyOS NEXT 实战案例七:List系列] 多列列表组件实战:打造精美应用推荐页 进阶篇
- 152.[HarmonyOS NEXT 实战案例十二:List系列] 卡片样式列表组件实战:打造精美电商应用 进阶篇
- 144.[HarmonyOS NEXT 实战案例十:List系列] 字母索引列表组件实战:打造高效联系人应用 进阶篇
- 140.[HarmonyOS NEXT 实战案例八:List系列] 滑动操作列表组件实战:打造高效待办事项应用 进阶篇
- 136.[HarmonyOS NEXT 实战案例七:List系列] 水平列表组件实战:打造精美图片库 进阶篇
- [HarmonyOS NEXT 实战案例:分割布局] 进阶篇 - 设置中心的动态内容与复用构建
- 147.[HarmonyOS NEXT 实战案例八 :List系列] 粘性头部列表基础篇
- 155.[HarmonyOS NEXT 实战案例十二 :List系列] 聊天消息列表 - 基础篇