温馨提示:本篇博客的详细代码已发布到 git : gitcode.com/nutpi/Harmo... 可以下载运行哦!
HarmonyOS NEXT UVList组件开发指南(四)
第四篇:UVList组件高级特性与性能优化
1. 高级特性
1.1 自定义渲染
除了使用默认的渲染方式外,UVList组件还支持自定义渲染,以满足更复杂的UI需求:
typescript
@Entry
@Component
struct CustomRenderDemo {
@State listItems: Array<ListItemProps> = [
{
title: '自定义项1',
note: '自定义渲染示例'
},
{
title: '自定义项2',
note: '自定义渲染示例'
}
];
// 自定义渲染函数
@Builder
private renderCustomItem(item: ListItemProps) {
Row() {
Circle({ width: 8, height: 8 })
.fill('#2979ff')
.margin({ right: 8 })
Column() {
Text(item.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
if (item.note) {
Text(item.note)
.fontSize(14)
.fontColor('#999')
.margin({ top: 4 })
}
}
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
}
build() {
Column() {
// 使用自定义渲染的列表
List() {
ForEach(this.listItems, (item: ListItemProps) => {
ListItem() {
this.renderCustomItem(item)
}
.padding({ left: 16, right: 16, top: 4, bottom: 4 })
})
}
.width('100%')
.divider({ strokeWidth: 1, color: '#f5f5f5', startMargin: 16, endMargin: 16 })
.borderRadius(8)
.backgroundColor(Color.White)
}
.width('100%')
.height('100%')
.backgroundColor('#f5f5f5')
.padding(16)
}
}
1.2 分组列表
通过扩展UVList组件,可以实现分组列表功能:
typescript
// 分组列表数据接口
interface GroupListProps {
// 分组标题
title?: string;
// 分组数据
groups: Array<{
// 分组标题
title: string;
// 分组项
items: Array<ListItemProps>;
}>;
}
@Component
struct GroupList {
private props: GroupListProps = {} as GroupListProps;
build() {
Column() {
// 列表标题(如果有)
if (this.props.title) {
Text(this.props.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.textAlign(TextAlign.Start)
.padding({ left: 16, right: 16, top: 16, bottom: 8 })
}
// 遍历分组
ForEach(this.props.groups, (group, groupIndex) => {
Column() {
// 分组标题
Text(group.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.width('100%')
.textAlign(TextAlign.Start)
.padding({ left: 16, right: 16, top: 16, bottom: 8 })
// 分组内容
UVList({
props: {
items: group.items
}
})
}
.margin({ top: groupIndex > 0 ? 16 : 0 })
})
}
.width('100%')
}
}
1.3 可展开列表
通过扩展UVList组件,可以实现可展开列表功能:
typescript
// 可展开列表项接口
interface ExpandableListItemProps extends ListItemProps {
// 子项
children?: Array<ListItemProps>;
// 是否展开
expanded?: boolean;
}
@Component
struct ExpandableListItem {
@ObjectLink item: ExpandableListItemProps;
build() {
Column() {
// 主项
Row() {
// 标题
Text(this.item.title)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
// 展开/收起图标
Image(this.item.expanded ? $r('app.media.ic_arrow_up') : $r('app.media.ic_arrow_down'))
.width(16)
.height(16)
.objectFit(ImageFit.Contain)
}
.width('100%')
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
.onClick(() => {
// 切换展开状态
this.item.expanded = !this.item.expanded;
})
// 子项(如果展开)
if (this.item.expanded && this.item.children) {
Column() {
ForEach(this.item.children, (child: ListItemProps) => {
UVListItem({
item: child,
showIcon: true,
showRightText: true,
navigable: true
})
})
}
.padding({ left: 16 })
}
}
.width('100%')
}
}
2. 性能优化
2.1 状态管理优化
typescript
// 优化前:整个列表数据作为响应式状态
@State listItems: Array<ListItemProps> = [];
// 优化后:将不变的数据与变化的状态分离
private listItemsData: Array<{
id: string;
title: string;
note: string;
icon?: string | Resource;
}> = [];
@State private listItemsState: Record<string, {
disabled: boolean;
expanded: boolean;
}> = {};
// 构建列表项
private getListItems(): Array<ListItemProps> {
return this.listItemsData.map(item => ({
...item,
disabled: this.listItemsState[item.id]?.disabled || false,
onClick: () => this.handleItemClick(item.id)
}));
}
优化说明:
- 将不变的数据(如标题、描述、图标)与变化的状态(如禁用状态、展开状态)分离
- 减少响应式数据的体积,提高性能
- 按需构建列表项,避免不必要的更新
2.2 渲染性能优化
typescript
// 优化前:直接在ForEach中渲染所有列表项
ForEach(this.listItems, (item: ListItemProps) => {
ListItem() {
UVListItem({
item: item,
// 其他属性
})
}
})
// 优化后:使用LazyForEach按需渲染列表项
LazyForEach(new DataSource(this.listItems), (item: ListItemProps) => {
ListItem() {
UVListItem({
item: item,
// 其他属性
})
}
}, item => item.id)
优化说明:
- 使用LazyForEach代替ForEach,实现按需渲染
- 只渲染可见区域的列表项,减少内存占用和提高渲染性能
- 为每个列表项提供唯一的key,优化更新性能
2.3 条件渲染优化
typescript
// 优化前:频繁的条件判断
build() {
Row() {
if (this.showIcon && this.item.icon) {
// 渲染图标
}
Column() {
Text(this.item.title)
if (this.item.note) {
Text(this.item.note)
}
}
Row() {
if (this.showRightText && this.item.rightText) {
Text(this.item.rightText)
}
if (this.navigable && this.item.showArrow !== false) {
Image($r('app.media.ic_arrow_right'))
}
}
}
}
// 优化后:提前计算条件结果
build() {
// 提前计算条件结果
const showIcon = this.showIcon && this.item.icon;
const showNote = this.item.note != null;
const showRightText = this.showRightText && this.item.rightText;
const showArrow = this.navigable && this.item.showArrow !== false;
Row() {
if (showIcon) {
// 渲染图标
}
Column() {
Text(this.item.title)
if (showNote) {
Text(this.item.note)
}
}
Row() {
if (showRightText) {
Text(this.item.rightText)
}
if (showArrow) {
Image($r('app.media.ic_arrow_right'))
}
}
}
}
优化说明:
- 提前计算条件结果,减少重复计算
- 简化条件判断,提高代码可读性
- 减少条件判断的复杂度,提高渲染性能
3. 内存优化
3.1 资源复用
typescript
// 优化前:每次渲染都创建新的样式对象
build() {
Row() {
// 内容
}
.width('100%')
.padding({ left: 16, right: 16, top: 12, bottom: 12 })
.backgroundColor(this.item.disabled ? '#fafafa' : Color.White)
.borderRadius(8)
}
// 优化后:复用样式对象
// 定义常量样式
private static readonly COMMON_STYLE = {
width: '100%',
borderRadius: 8,
padding: { left: 16, right: 16, top: 12, bottom: 12 }
};
private static readonly NORMAL_BG = Color.White;
private static readonly DISABLED_BG = '#fafafa';
build() {
Row() {
// 内容
}
.width(UVListItem.COMMON_STYLE.width)
.padding(UVListItem.COMMON_STYLE.padding)
.backgroundColor(this.item.disabled ? UVListItem.DISABLED_BG : UVListItem.NORMAL_BG)
.borderRadius(UVListItem.COMMON_STYLE.borderRadius)
}
优化说明:
- 使用静态常量定义样式,避免重复创建样式对象
- 复用样式对象,减少内存占用
- 提高样式一致性,便于维护
3.2 图片资源优化
typescript
// 优化前:直接使用图片资源
Image(this.item.icon)
.width(24)
.height(24)
.objectFit(ImageFit.Contain)
// 优化后:根据需要加载不同尺寸的图片
private getOptimizedIcon(icon: string | Resource): string | Resource {
if (typeof icon === 'string') {
return icon;
}
// 根据当前设备分辨率选择合适的图片资源
const devicePixelRatio = getDevicePixelRatio();
if (devicePixelRatio <= 1) {
return $r('app.media.icon_small');
} else if (devicePixelRatio <= 2) {
return $r('app.media.icon_medium');
} else {
return $r('app.media.icon_large');
}
}
// 使用优化后的图片资源
Image(this.getOptimizedIcon(this.item.icon))
.width(24)
.height(24)
.objectFit(ImageFit.Contain)
优化说明:
- 根据设备分辨率加载不同尺寸的图片资源
- 减少内存占用,提高渲染性能
- 优化图片显示效果
4. 交互优化
4.1 点击反馈优化
typescript
// 优化前:简单的点击处理
.onClick(() => {
if (!this.item.disabled) {
// 执行点击操作
this.item.onClick?.();
}
})
// 优化后:添加点击反馈效果
.onClick(() => {
if (!this.item.disabled) {
// 执行点击操作
this.item.onClick?.();
}
})
.stateStyles({
pressed: {
opacity: 0.7,
backgroundColor: '#f0f0f0'
},
disabled: {
opacity: 0.5,
backgroundColor: '#fafafa'
}
})
优化说明:
- 添加状态样式,提供按下和禁用状态的视觉反馈
- 使用opacity和backgroundColor变化提供明显的交互反馈
- 提高用户体验,增强交互感知
4.2 手势交互优化
typescript
// 优化前:仅支持点击操作
.onClick(() => {
// 点击处理
})
// 优化后:支持多种手势交互
.gesture(
GestureGroup(GestureMode.Exclusive,
TapGesture()
.onAction(() => {
// 点击处理
if (!this.item.disabled) {
this.item.onClick?.();
}
}),
LongPressGesture()
.onAction(() => {
// 长按处理
if (!this.item.disabled && this.item.onLongPress) {
this.item.onLongPress();
}
}),
SwipeGesture()
.onAction((event: SwipeGestureEvent) => {
// 滑动处理
if (!this.item.disabled && this.item.onSwipe) {
this.item.onSwipe(event);
}
})
)
)
优化说明:
- 支持点击、长按、滑动等多种手势交互
- 使用GestureGroup组合多种手势,并设置为互斥模式
- 为不同手势提供不同的处理函数,增强交互丰富性
4.3 动画效果优化
typescript
// 优化前:无动画效果
@Component
struct UVListItem {
// 组件属性
build() {
// 构建UI
}
}
// 优化后:添加动画效果
@Component
struct UVListItem {
// 组件属性
@State private isPressed: boolean = false;
@State private isAnimating: boolean = false;
build() {
Row() {
// 列表项内容
}
.width('100%')
.padding(16)
.backgroundColor(this.isPressed ? '#f0f0f0' : Color.White)
.borderRadius(8)
.opacity(this.item.disabled ? 0.5 : 1)
.animation({
duration: 200,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Normal
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.isPressed = true;
} else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
this.isPressed = false;
}
})
.onClick(() => {
if (!this.item.disabled) {
// 点击动画
this.isAnimating = true;
animateTo({
duration: 100,
onFinish: () => {
this.isAnimating = false;
// 执行点击操作
this.item.onClick?.();
}
}, () => {
this.isPressed = true;
})
animateTo({
duration: 100,
delay: 100
}, () => {
this.isPressed = false;
})
}
})
}
}
优化说明:
- 添加按下状态和动画状态管理
- 使用animation属性为样式变化添加过渡效果
- 使用animateTo实现点击时的动画序列
- 通过onTouch事件精确控制按下状态
5. 总结与最佳实践
在本篇文章中,我们详细介绍了UVList组件的高级特性与性能优化技巧。通过这些优化,可以显著提升UVList组件的性能和用户体验。 通过本系列文章的学习,相信你已经掌握了UVList组件的开发和优化技巧。在实际项目中,可以根据具体需求灵活运用这些技巧,打造出高性能、高体验的列表组件。 下一章节我们将主要讲解实际的应用场景,敬请期待!