HarmonyOS鸿蒙Next开发者技术支持骨架屏实现案例
HarmonyOS鸿蒙Next开发者技术支持骨架屏实现案例
案例概述
骨架屏(Skeleton Screen)是一种在数据加载期间显示的页面框架,提升用户体验。本案例演示如何使用HarmonyOS最新API实现一个优雅的骨架屏效果。
一、架构设计
1.1 核心组件
// SkeletonScreen.ets
// 骨架屏核心组件,包含动画和布局
@Component
export struct SkeletonScreen { }
// ShimmerEffect.ets
// 流光动画效果组件
@Component
export struct ShimmerEffect { }
// SkeletonManager.ets
// 骨架屏状态管理器
export class SkeletonManager { }
二、实现步骤详解
步骤1:定义数据模型和配置
// 1. 定义骨架屏项类型
export interface SkeletonItem {
type: 'rectangle' | 'circle' | 'text-line' | 'custom';
width: Length;
height: Length;
borderRadius?: number;
margin?: Padding | Margin;
animationDelay?: number; // 动画延迟
}
// 2. 动画配置
export interface SkeletonAnimationConfig {
shimmerDuration: number; // 流光动画时长
fadeDuration: number; // 淡入淡出时长
shimmerWidth: number; // 流光宽度
shimmerColor: ResourceColor; // 流光颜色
baseColor: ResourceColor; // 基础颜色
highlightColor: ResourceColor; // 高亮颜色
}
// 3. 骨架屏配置
export class SkeletonConfig {
static readonly DEFAULT_CONFIG: SkeletonAnimationConfig = {
shimmerDuration: 1500,
fadeDuration: 500,
shimmerWidth: 100,
shimmerColor: '#FFFFFF40',
baseColor: '#F0F0F0',
highlightColor: '#F5F5F5'
};
}
- 定义骨架屏的数据结构,支持多种形状类型
- 配置动画参数,便于统一管理
- 使用TypeScript接口确保类型安全
步骤2:实现流光动画效果
// ShimmerEffect.ets
@Component
export struct ShimmerEffect {
@State private shimmerOffset: number = -100; // 流光位置
// 动画控制器
private animationController: animation.Animator = new animation.Animator();
@Prop config: SkeletonAnimationConfig = SkeletonConfig.DEFAULT_CONFIG;
@Prop isActive: boolean = true; // 是否激活动画
aboutToAppear() {
if (this.isActive) {
this.startShimmerAnimation();
}
}
// 启动流光动画
private startShimmerAnimation(): void {
this.animationController.update({
duration: this.config.shimmerDuration,
iterations: -1, // 无限循环
curve: animation.Curve.Linear
});
this.animationController.onFrame((value: number) => {
this.shimmerOffset = 200 * value - 100; // 计算流光位置
});
this.animationController.play();
}
build() {
// 使用LinearGradient实现流光效果
Row()
.width(this.config.shimmerWidth)
.height('100%')
.backgroundImage(
new LinearGradient({
angle: 0,
colors: [
[Color.Transparent, 0],
[this.config.shimmerColor, 0.5],
[Color.Transparent, 1]
]
})
)
.translate({ x: `${this.shimmerOffset}%` })
}
}
- 使用HarmonyOS的
animation.Animator实现平滑动画 - 通过
LinearGradient创建渐变流光效果 - 支持无限循环动画,提升加载体验
步骤3:实现基础骨架屏项
// SkeletonItem.ets
@Component
export struct SkeletonItem {
@State private isAnimating: boolean = false;
@Prop itemConfig: SkeletonItem; // 骨架项配置
@Prop animationConfig: SkeletonAnimationConfig = SkeletonConfig.DEFAULT_CONFIG;
@Prop enableShimmer: boolean = true; // 是否启用流光效果
aboutToAppear() {
// 延迟启动动画
setTimeout(() => {
this.isAnimating = true;
}, this.itemConfig.animationDelay || 0);
}
@Builder
private buildSkeletonContent() {
// 根据类型构建不同的骨架形状
switch (this.itemConfig.type) {
case 'rectangle':
this.buildRectangle();
break;
case 'circle':
this.buildCircle();
break;
case 'text-line':
this.buildTextLine();
break;
default:
this.buildRectangle();
}
}
@Builder
private buildRectangle() {
Column()
.width(this.itemConfig.width)
.height(this.itemConfig.height)
.backgroundColor(this.animationConfig.baseColor)
.borderRadius(this.itemConfig.borderRadius || 4)
.overflow(Overflow.Hidden) // 重要:确保流光不溢出
.overlay(
// 流光效果叠加层
this.enableShimmer && this.isAnimating ?
ShimmerEffect({
config: this.animationConfig,
isActive: this.isAnimating
}) : null
)
}
@Builder
private buildCircle() {
Circle()
.width(this.itemConfig.width)
.height(this.itemConfig.height)
.fill(this.animationConfig.baseColor)
.overlay(
this.enableShimmer && this.isAnimating ?
ShimmerEffect({
config: this.animationConfig,
isActive: this.isAnimating
}) : null
)
}
@Builder
private buildTextLine() {
// 文本行骨架,模拟段落
Column({ space: 4 }) {
Rectangle()
.width('100%')
.height(16)
.fill(this.animationConfig.baseColor)
.borderRadius(8)
Rectangle()
.width('80%')
.height(16)
.fill(this.animationConfig.baseColor)
.borderRadius(8)
Rectangle()
.width('60%')
.height(16)
.fill(this.animationConfig.baseColor)
.borderRadius(8)
}
}
build() {
Column()
.margin(this.itemConfig.margin || {})
.opacity(this.isAnimating ? 1 : 0)
.animation({
duration: this.animationConfig.fadeDuration,
curve: animation.Curve.EaseOut
})
{
this.buildSkeletonContent()
}
}
}
- 支持多种骨架形状:矩形、圆形、文本行
- 使用
overlay属性叠加流光效果 - 实现淡入动画,提升视觉体验
- 可配置延迟动画,创建错落有致的加载效果
步骤4:实现完整骨架屏组件
// SkeletonScreen.ets
@Component
export struct SkeletonScreen {
@State private isLoading: boolean = true; // 加载状态
// 骨架布局配置
@Prop skeletonLayout: SkeletonItem[] = [];
@Prop contentBuilder: CustomBuilder; // 实际内容构建器
@Prop animationConfig: SkeletonAnimationConfig = SkeletonConfig.DEFAULT_CONFIG;
@Prop showShimmer: boolean = true; // 是否显示流光
// 加载完成回调
@Prop onLoadComplete?: () => void;
// 模拟数据加载
async loadData(): Promise<void> {
// 显示骨架屏
this.isLoading = true;
try {
// 模拟异步数据加载
await this.simulateDataFetch();
// 数据加载完成
this.isLoading = false;
this.onLoadComplete?.();
} catch (error) {
// 处理错误
this.isLoading = false;
}
}
private async simulateDataFetch(): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, 2000); // 模拟2秒加载
});
}
@Builder
private buildSkeletonLayout() {
Column({ space: 12 }) {
ForEach(this.skeletonLayout, (item: SkeletonItem, index: number) => {
SkeletonItem({
itemConfig: {
...item,
animationDelay: index * 100 // 错开动画延迟
},
animationConfig: this.animationConfig,
enableShimmer: this.showShimmer
})
})
}
.width('100%')
.padding(16)
}
@Builder
private buildContent() {
// 使用@BuilderParam构建实际内容
this.contentBuilder()
}
aboutToAppear() {
// 组件出现时开始加载
this.loadData();
}
build() {
Stack({ alignContent: Alignment.TopStart }) {
// 实际内容
this.buildContent()
.opacity(this.isLoading ? 0 : 1)
.animation({
duration: 300,
curve: animation.Curve.EaseInOut
})
// 骨架屏
this.buildSkeletonLayout()
.opacity(this.isLoading ? 1 : 0)
.animation({
duration: 300,
curve: animation.Curve.EaseInOut
})
}
.width('100%')
.height('100%')
}
}
- 使用
Stack层叠布局,切换骨架屏和实际内容 - 支持自定义布局配置,灵活适配不同页面
- 实现平滑的淡入淡出过渡效果
- 提供异步数据加载接口
步骤5:实现状态管理器
// SkeletonManager.ets
export class SkeletonManager {
private static instance: SkeletonManager;
private loadingStates: Map<string, boolean> = new Map();
private loadingCallbacks: Map<string, Array<() => void>> = new Map();
// 单例模式
static getInstance(): SkeletonManager {
if (!SkeletonManager.instance) {
SkeletonManager.instance = new SkeletonManager();
}
return SkeletonManager.instance;
}
// 开始加载
startLoading(componentId: string): void {
this.loadingStates.set(componentId, true);
this.notifyStateChange(componentId);
}
// 完成加载
finishLoading(componentId: string): void {
this.loadingStates.set(componentId, false);
this.notifyStateChange(componentId);
}
// 获取加载状态
isLoading(componentId: string): boolean {
return this.loadingStates.get(componentId) || false;
}
// 注册状态监听
registerListener(componentId: string, callback: () => void): void {
if (!this.loadingCallbacks.has(componentId)) {
this.loadingCallbacks.set(componentId, []);
}
this.loadingCallbacks.get(componentId)!.push(callback);
}
private notifyStateChange(componentId: string): void {
const callbacks = this.loadingCallbacks.get(componentId) || [];
callbacks.forEach(callback => callback());
}
}
- 单例模式管理全局加载状态
- 支持多组件独立加载控制
- 提供状态监听机制
- 便于组件间通信
步骤6:使用示例
// UserProfileSkeleton.ets
@Entry
@Component
export struct UserProfileSkeleton {
// 定义骨架布局
private skeletonLayout: SkeletonItem[] = [
{
type: 'circle',
width: 80,
height: 80,
margin: { top: 20, bottom: 16 }
},
{
type: 'rectangle',
width: '40%',
height: 24,
borderRadius: 12,
margin: { bottom: 8 }
},
{
type: 'rectangle',
width: '60%',
height: 16,
borderRadius: 8,
margin: { bottom: 24 }
},
{
type: 'text-line',
width: '100%',
height: 100
}
];
@Builder
private buildUserProfile() {
Column({ space: 12 }) {
// 用户头像
Image($r('app.media.user_avatar'))
.width(80)
.height(80)
.borderRadius(40)
.objectFit(ImageFit.Cover)
// 用户名
Text('张三')
.fontSize(20)
.fontWeight(FontWeight.Bold)
// 用户描述
Text('高级软件工程师 | HarmonyOS开发者')
.fontSize(14)
.fontColor('#666666')
// 用户简介
Text('专注于移动应用开发,拥有丰富的跨平台开发经验。')
.fontSize(16)
.lineHeight(24)
}
.padding(16)
}
build() {
Column() {
SkeletonScreen({
skeletonLayout: this.skeletonLayout,
contentBuilder: () => this.buildUserProfile(),
animationConfig: {
...SkeletonConfig.DEFAULT_CONFIG,
shimmerColor: '#4D94FF40' // 自定义流光颜色
},
showShimmer: true,
onLoadComplete: () => {
console.log('用户资料加载完成');
}
})
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
}
- 定义具体的骨架屏布局配置
- 通过
contentBuilder传入实际内容 - 可自定义动画参数
- 提供加载完成回调
三、高级特性扩展
3.1 列表骨架屏
// ListSkeleton.ets
@Component
export struct ListSkeleton {
@Prop itemCount: number = 5; // 骨架项数量
@Builder
private buildListItemSkeleton(index: number) {
Row({ space: 12 }) {
// 左侧图片
SkeletonItem({
itemConfig: {
type: 'rectangle',
width: 60,
height: 60,
borderRadius: 8
}
})
// 右侧内容
Column({ space: 6 }) {
SkeletonItem({
itemConfig: {
type: 'rectangle',
width: '70%',
height: 16,
borderRadius: 8
}
})
SkeletonItem({
itemConfig: {
type: 'rectangle',
width: '50%',
height: 12,
borderRadius: 6
}
})
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
}
.padding({ top: 12, bottom: 12 })
}
build() {
List({ space: 8 }) {
ForEach(Array.from({ length: this.itemCount }), (_, index: number) => {
ListItem() {
this.buildListItemSkeleton(index)
}
})
}
}
}
3.2 网格骨架屏
// GridSkeleton.ets
@Component
export struct GridSkeleton {
@Prop columns: number = 2; // 列数
@Prop itemCount: number = 6; // 项目数
@Builder
private buildGridItemSkeleton() {
Column({ space: 8 }) {
// 图片区域
SkeletonItem({
itemConfig: {
type: 'rectangle',
width: '100%',
height: 120,
borderRadius: 8
}
})
// 标题
SkeletonItem({
itemConfig: {
type: 'rectangle',
width: '80%',
height: 16,
borderRadius: 8
}
})
// 价格
SkeletonItem({
itemConfig: {
type: 'rectangle',
width: '40%',
height: 14,
borderRadius: 7
}
})
}
}
build() {
GridRow({ columns: this.columns, gutter: 8 }) {
ForEach(Array.from({ length: this.itemCount }), (_, index: number) => {
GridCol({ span: 1 }) {
this.buildGridItemSkeleton()
}
})
}
}
}
四、最佳实践建议
4.1 性能优化
- 动画性能
- 使用硬件加速的动画
- 避免过多的同时动画
- 适时停止不必要的动画
- 内存管理
- 及时清理动画资源
- 使用对象池复用骨架项
- 控制骨架屏显示时长
4.2 用户体验
- 设计原则
- 骨架屏布局应与实际内容一致
- 动画要自然流畅
- 提供加载超时处理
- 错误处理
- 加载失败时提供重试机制
- 网络异常时显示适当提示
- 支持手动刷新
4.3 可访问性
// 为屏幕阅读器提供提示
.accessibilityDescription('内容加载中,请稍候')
.accessibilityState(AccessibilityState.Disabled)
五、总结
核心优势
- 用户体验好:避免白屏,提供视觉连续性
- 性能优秀:使用HarmonyOS原生动画系统
- 灵活可配:支持多种布局和动画效果
- 易于集成:提供简洁的API接口
使用场景
- 网络请求数据加载
- 图片懒加载
- 复杂组件初始化
- 首屏性能优化
更多关于HarmonyOS鸿蒙Next开发者技术支持骨架屏实现案例的实战教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next中骨架屏可通过ArkUI的LoadingProgress组件或自定义组件实现。使用@State装饰器管理加载状态,结合条件渲染控制显示逻辑。布局采用Column、Row等容器组件构建骨架结构,设置透明度动画增强视觉效果。关键代码涉及组件封装与数据绑定,需遵循鸿蒙UI开发规范。
更多关于HarmonyOS鸿蒙Next开发者技术支持骨架屏实现案例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
这是一个非常专业和完整的HarmonyOS Next骨架屏实现方案。案例结构清晰,代码质量高,充分利用了ArkTS和HarmonyOS的API特性。以下是对该实现的技术点评:
架构设计的优点:
- 模块化清晰:将核心组件(SkeletonScreen)、动画效果(ShimmerEffect)和状态管理(SkeletonManager)分离,符合高内聚、低耦合的设计原则。
- 类型安全:使用TypeScript接口(
SkeletonItem,SkeletonAnimationConfig)定义数据结构,提高了代码的可靠性和开发体验。 - 配置化:通过
SkeletonConfig集中管理动画参数,易于维护和主题定制。
关键技术实现亮点:
- 动画系统:正确使用了
animation.Animator实现平滑的流光(shimmer)动画,并通过LinearGradient创建视觉上流畅的渐变效果。设置iterations: -1实现无限循环符合骨架屏场景。 - 布局与叠加:在
SkeletonItem中使用overlay属性将ShimmerEffect叠加在基础形状之上,并通过overflow: Hidden限制溢出,这是实现局部流光效果的关键。 - 状态驱动:利用
@State、@Prop装饰器管理组件的加载状态和动画状态,与ArkUI的声明式UI范式紧密结合。 - 交错动画:在
SkeletonScreen的buildSkeletonLayout中,通过index * 100为每个骨架项设置不同的animationDelay,创造了错落有致的加载动效,提升了视觉体验。 - 组件通信:
SkeletonManager采用单例模式,提供了跨组件的加载状态管理能力,适合复杂页面中多个独立加载模块的场景。
高级特性与最佳实践:
- 扩展性强:提供的
ListSkeleton和GridSkeleton示例展示了如何快速适配列表和网格布局,实践性很强。 - 性能考虑:提到了动画性能优化、内存管理(如对象池)和适时停止动画,这些都是生产环境中必须关注的点。
- 可访问性:考虑到了屏幕阅读器,通过
.accessibilityDescription提供提示,体现了完善的产品思维。
总结: 这份案例远超简单的示例,提供了一个企业级、可复用的骨架屏解决方案。它不仅演示了如何实现效果,更展示了如何在HarmonyOS Next中构建一个结构良好、可配置、高性能的UI组件。开发者可以直接参考此架构,根据实际业务需求调整布局配置和动画细节,快速集成到自己的项目中。

