鸿蒙APP开发从入门到精通:ArkUI组件库详解与常用组件实战

《鸿蒙APP开发从入门到精通》第2篇:ArkUI组件库详解与常用组件实战 🎨


内容承接与核心价值

这是《鸿蒙APP开发从入门到精通》的第2篇------组件进阶篇 ,承接第1篇的「Hello World!」基础,100%复用项目架构 ,为后续第6-12篇的电商购物车全栈项目铺垫核心组件基础。

学习目标

  • 理解ArkUI组件的分类与核心特性;
  • 掌握15个常用ArkUI组件的用法(布局容器、文本、按钮、输入框、列表、网格等);
  • 实现一个电商首页组件(包含轮播图、分类导航、商品列表、底部导航栏);
  • 优化组件交互效果(悬停、点击、滑动动画)。

学习重点

  • 弹性布局(Flex)与网格布局(Grid)的深度应用;
  • 自定义组件与系统组件的结合使用;
  • 响应式布局的实现(适配不同设备尺寸)。

一、 ArkUI组件库概述 📚

1.1 组件分类

HarmonyOS Next的ArkUI组件库分为基础组件高级组件

分类 组件示例
基础组件 Text、Image、Button、Input、TextArea、Checkbox、Radio、Slider、Switch
容器组件 Column、Row、Flex、Grid、GridItem、List、ListItem、Stack
高级组件 Swiper(轮播图)、TabContent(标签页)、Carousel(卡片轮播)、WaterFlow(瀑布流)

1.2 核心特性

  1. 声明式UI:通过代码描述UI结构,编译器自动渲染;
  2. 数据驱动:组件属性与状态数据绑定,数据变化自动更新UI;
  3. 响应式布局:自动适配不同设备尺寸;
  4. 组件复用:支持自定义组件,提高代码复用率;
  5. 高性能渲染:采用方舟编译器编译,渲染效率比传统Web开发高10-20倍。

二、 常用组件实战 🛠️

2.1 实战目标

基于第1篇的「MyFirstHarmonyApp」项目架构,创建一个电商首页组件,包含以下功能:

  • 轮播图(Swiper):展示促销活动;
  • 分类导航(Grid):显示商品分类;
  • 商品列表(List):展示热门商品;
  • 底部导航栏(TabBar):实现页面切换。

2.2 🔧 项目结构调整

在「entry/src/main/ets」目录下创建以下文件夹:

  • components:存放自定义组件;
  • models:存放数据模型;
  • utils:存放工具函数。

2.3 🔧 数据模型与工具函数

1. 数据模型

⌨️ entry/src/main/ets/models/HomeModel.ets

typescript 复制代码
// 轮播图数据模型
export interface BannerModel {
  id: number;
  imageUrl: string;
  linkUrl: string;
}

// 分类导航数据模型
export interface CategoryModel {
  id: number;
  name: string;
  iconUrl: string;
}

// 商品数据模型
export interface GoodsModel {
  id: number;
  name: string;
  imageUrl: string;
  price: number;
  originalPrice: number;
  sales: number;
}

// 底部导航栏数据模型
export interface TabBarModel {
  id: number;
  name: string;
  iconUrl: string;
  selectedIconUrl: string;
  pageUrl: string;
}
2. 工具函数

⌨️ entry/src/main/ets/utils/ImageUtils.ets

typescript 复制代码
// 图片加载工具函数
export function getImageUrl(imagePath: string): string {
  return `$r('app.media.${imagePath}')`;
}
3. 模拟数据

⌨️ entry/src/main/ets/models/HomeData.ets

typescript 复制代码
import { BannerModel, CategoryModel, GoodsModel, TabBarModel } from './HomeModel';
import { getImageUrl } from '../utils/ImageUtils';

// 轮播图模拟数据
export const bannerData: Array<BannerModel> = [
  { id: 1, imageUrl: getImageUrl('banner1'), linkUrl: 'pages/ProductDetailPage' },
  { id: 2, imageUrl: getImageUrl('banner2'), linkUrl: 'pages/ProductDetailPage' },
  { id: 3, imageUrl: getImageUrl('banner3'), linkUrl: 'pages/ProductDetailPage' }
];

// 分类导航模拟数据
export const categoryData: Array<CategoryModel> = [
  { id: 1, name: '手机', iconUrl: getImageUrl('category1') },
  { id: 2, name: '平板', iconUrl: getImageUrl('category2') },
  { id: 3, name: '笔记本', iconUrl: getImageUrl('category3') },
  { id: 4, name: '智能穿戴', iconUrl: getImageUrl('category4') },
  { id: 5, name: '家居', iconUrl: getImageUrl('category5') },
  { id: 6, name: '出行', iconUrl: getImageUrl('category6') }
];

// 商品模拟数据
export const goodsData: Array<GoodsModel> = [
  { id: 1, name: '华为Mate 60 Pro', imageUrl: getImageUrl('goods1'), price: 6999, originalPrice: 7999, sales: 12345 },
  { id: 2, name: '华为P60 Pro', imageUrl: getImageUrl('goods2'), price: 5999, originalPrice: 6999, sales: 8765 },
  { id: 3, name: '华为nova 12 Pro', imageUrl: getImageUrl('goods3'), price: 3999, originalPrice: 4999, sales: 6543 },
  { id: 4, name: '华为MateBook X Pro', imageUrl: getImageUrl('goods4'), price: 8999, originalPrice: 9999, sales: 4321 },
  { id: 5, name: '华为Watch GT 4', imageUrl: getImageUrl('goods5'), price: 1499, originalPrice: 1699, sales: 2109 }
];

// 底部导航栏模拟数据
export const tabBarData: Array<TabBarModel> = [
  { id: 1, name: '首页', iconUrl: getImageUrl('tab1'), selectedIconUrl: getImageUrl('tab1_selected'), pageUrl: 'pages/Index' },
  { id: 2, name: '分类', iconUrl: getImageUrl('tab2'), selectedIconUrl: getImageUrl('tab2_selected'), pageUrl: 'pages/CategoryPage' },
  { id: 3, name: '购物车', iconUrl: getImageUrl('tab3'), selectedIconUrl: getImageUrl('tab3_selected'), pageUrl: 'pages/CartPage' },
  { id: 4, name: '我的', iconUrl: getImageUrl('tab4'), selectedIconUrl: getImageUrl('tab4_selected'), pageUrl: 'pages/MyPage' }
];

2.4 🔧 自定义组件实现

1. 轮播图组件

⌨️ entry/src/main/ets/components/BannerComponent.ets

typescript 复制代码
import { BannerModel } from '../models/HomeModel';
import router from '@ohos.router';

@Component
export struct BannerComponent {
  @Prop data: Array<BannerModel> = [];

  build() {
    Swiper() {
      ForEach(this.data, (item: BannerModel) => {
        Image(item.imageUrl)
          .width('100%')
          .height(200)
          .objectFit(ImageFit.Cover)
          .borderRadius(12)
          .onClick(() => {
            router.pushUrl({ url: item.linkUrl });
          });
      }, (item: BannerModel) => item.id.toString());
    }
    .width('100%')
    .height(200)
    .autoPlay(true)
    .indicator({
      style: IndicatorStyle.Number,
      isShow: true,
      size: 12
    })
    .loop(true)
    .duration(3000);
  }
}
2. 分类导航组件

⌨️ entry/src/main/ets/components/CategoryComponent.ets

typescript 复制代码
import { CategoryModel } from '../models/HomeModel';
import router from '@ohos.router';

@Component
export struct CategoryComponent {
  @Prop data: Array<CategoryModel> = [];

  build() {
    Grid() {
      ForEach(this.data, (item: CategoryModel) => {
        GridItem() {
          Column({ space: 8 }) {
            Image(item.iconUrl)
              .width(50)
              .height(50)
              .objectFit(ImageFit.Contain);
            Text(item.name)
              .fontSize(14)
              .textColor('#666666');
          }
          .width('100%')
          .height('100%')
          .onClick(() => {
            router.pushUrl({ url: 'pages/CategoryPage' });
          });
        }
        .width('33.33%')
        .height('auto');
      }, (item: CategoryModel) => item.id.toString());
    }
    .width('100%')
    .height('auto')
    .columnsTemplate('1fr 1fr 1fr')
    .rowsTemplate('1fr 1fr')
    .columnsGap(20)
    .rowsGap(20);
  }
}
3. 商品列表组件

⌨️ entry/src/main/ets/components/GoodsListComponent.ets

typescript 复制代码
import { GoodsModel } from '../models/HomeModel';
import router from '@ohos.router';

@Component
export struct GoodsListComponent {
  @Prop data: Array<GoodsModel> = [];

  build() {
    List({ space: 16 }) {
      ForEach(this.data, (item: GoodsModel) => {
        ListItem() {
          GoodsItemComponent({ goods: item });
        }
        .width('100%')
        .height('auto');
      }, (item: GoodsModel) => item.id.toString());
    }
    .width('100%')
    .height('auto')
    .padding(0, 16, 0, 16);
  }
}

@Component
struct GoodsItemComponent {
  @Prop goods: GoodsModel;

  build() {
    Column({ space: 12 }) {
      // 商品图片
      Image(this.goods.imageUrl)
        .width('100%')
        .height(200)
        .objectFit(ImageFit.Cover)
        .borderRadius(12);

      // 商品信息
      Column({ space: 8 }) {
        Text(this.goods.name)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
          .textColor('#000000')
          .maxLines(2)
          .ellipsis({ overflow: TextOverflow.Ellipsis });

        // 价格与销量
        Row({ space: 16 }) {
          Column({ space: 4 }) {
            Text(`¥${this.goods.price}`)
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .textColor('#FF0000');
            Text(`¥${this.goods.originalPrice}`)
              .fontSize(14)
              .textColor('#999999')
              .decoration({ type: TextDecorationType.LineThrough });
          }
          .alignItems(HorizontalAlign.Start);

          Text(`已售${this.goods.sales}`)
            .fontSize(14)
            .textColor('#666666');
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween);
      }
      .width('100%')
      .alignItems(HorizontalAlign.Start)
      .padding(0, 0, 0, 8);
    }
    .width('100%')
    .height('auto')
    .padding(16)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .onClick(() => {
      router.pushUrl({ url: 'pages/ProductDetailPage' });
    });
  }
}
4. 底部导航栏组件

⌨️ entry/src/main/ets/components/TabBarComponent.ets

typescript 复制代码
import { TabBarModel } from '../models/HomeModel';
import router from '@ohos.router';

@Component
export struct TabBarComponent {
  @Prop data: Array<TabBarModel> = [];
  @Prop selectedIndex: number = 0;

  build() {
    Row({ space: 0 }) {
      ForEach(this.data, (item: TabBarModel, index: number) => {
        Column({ space: 4 }) {
          Image(index === this.selectedIndex ? item.selectedIconUrl : item.iconUrl)
            .width(24)
            .height(24)
            .objectFit(ImageFit.Contain);
          Text(item.name)
            .fontSize(12)
            .textColor(index === this.selectedIndex ? '#007DFF' : '#666666');
        }
        .width('25%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .onClick(() => {
          router.pushUrl({ url: item.pageUrl });
        });
      }, (item: TabBarModel) => item.id.toString());
    }
    .width('100%')
    .height(56)
    .backgroundColor('#FFFFFF')
    .borderRadius(16, 16, 0, 0)
    .padding(0, 8, 0, 8);
  }
}

2.5 🔧 首页页面实现

⌨️ entry/src/main/ets/pages/Index.ets(替换默认代码)

typescript 复制代码
import { BannerComponent } from '../components/BannerComponent';
import { CategoryComponent } from '../components/CategoryComponent';
import { GoodsListComponent } from '../components/GoodsListComponent';
import { TabBarComponent } from '../components/TabBarComponent';
import { bannerData, categoryData, goodsData, tabBarData } from '../models/HomeData';

@Entry
@Component
struct Index {
  @State selectedIndex: number = 0;

  build() {
    Column({ space: 0 }) {
      // 页面内容
      Scroll() {
        Column({ space: 24 }) {
          // 轮播图
          BannerComponent({ data: bannerData });

          // 分类导航
          CategoryComponent({ data: categoryData });

          // 热门商品标题
          Text('热门商品')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .textColor('#000000')
            .width('100%')
            .padding(0, 0, 0, 16);

          // 商品列表
          GoodsListComponent({ data: goodsData });
        }
        .width('100%')
        .padding(20);
      }
      .width('100%')
      .height('100%')
      .layoutWeight(1);

      // 底部导航栏
      TabBarComponent({
        data: tabBarData,
        selectedIndex: this.selectedIndex
      });
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5');
  }
}

三、 组件交互效果优化 🎬

3.1 悬停动画

为商品列表组件添加悬停动画,提升用户体验:

⌨️ entry/src/main/ets/components/GoodsListComponent.ets(修改GoodsItemComponent)

typescript 复制代码
@Component
struct GoodsItemComponent {
  @Prop goods: GoodsModel;
  @State isHover: boolean = false;

  build() {
    Column({ space: 12 }) {
      // 商品图片
      Image(this.goods.imageUrl)
        .width('100%')
        .height(200)
        .objectFit(ImageFit.Cover)
        .borderRadius(12)
        .scale({ x: this.isHover ? 1.05 : 1, y: this.isHover ? 1.05 : 1 })
        .transition({ duration: 300, curve: Curve.EaseOut });

      // 商品信息
      // ...
    }
    .width('100%')
    .height('auto')
    .padding(16)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .onClick(() => {
      router.pushUrl({ url: 'pages/ProductDetailPage' });
    })
    .onHover(() => {
      this.isHover = true;
    })
    .onHoverEnd(() => {
      this.isHover = false;
    });
  }
}

3.2 点击动画

为按钮组件添加点击动画,反馈用户操作:

⌨️ entry/src/main/ets/components/GoodsListComponent.ets(添加立即购买按钮)

typescript 复制代码
@Component
struct GoodsItemComponent {
  @Prop goods: GoodsModel;
  @State isHover: boolean = false;
  @State isPressed: boolean = false;

  build() {
    Column({ space: 12 }) {
      // 商品图片
      // ...

      // 商品信息
      Column({ space: 8 }) {
        // 商品名称
        // ...

        // 价格与销量
        // ...

        // 立即购买按钮
        Button('立即购买')
          .width('100%')
          .height(48)
          .backgroundColor('#007DFF')
          .onClick(() => {
            console.log('点击了立即购买按钮');
          })
          .onPress(() => {
            this.isPressed = true;
          })
          .onPressEnd(() => {
            this.isPressed = false;
          })
          .scale({ x: this.isPressed ? 0.95 : 1, y: this.isPressed ? 0.95 : 1 })
          .transition({ duration: 100, curve: Curve.EaseOut });
      }
      .width('100%')
      .alignItems(HorizontalAlign.Start)
      .padding(0, 0, 0, 8);
    }
    .width('100%')
    .height('auto')
    .padding(16)
    .backgroundColor('#FFFFFF')
    .borderRadius(12)
    .onClick(() => {
      router.pushUrl({ url: 'pages/ProductDetailPage' });
    })
    .onHover(() => {
      this.isHover = true;
    })
    .onHoverEnd(() => {
      this.isHover = false;
    });
  }
}

四、 项目运行与效果验证 📱

4.1 图片资源准备

在「entry/src/main/resources/base/media」目录下添加以下图片资源(图片格式为.png,尺寸符合要求):

  • banner1.pngbanner2.pngbanner3.png(轮播图);
  • category1.pngcategory2.pngcategory3.pngcategory4.pngcategory5.pngcategory6.png(分类导航);
  • goods1.pnggoods2.pnggoods3.pnggoods4.pnggoods5.png(商品列表);
  • tab1.pngtab1_selected.pngtab2.pngtab2_selected.pngtab3.pngtab3_selected.pngtab4.pngtab4_selected.png(底部导航栏)。

4.2 🔧 项目运行

① 在DevEco Studio中点击「Run」按钮;

② 选择调试设备,点击「OK」;

③ 等待编译安装完成,应用会自动在设备上启动。

效果验证

轮播图 :自动轮播促销活动,点击可跳转;

分类导航 :显示6个商品分类,点击可跳转;

商品列表 :显示5个热门商品,悬停有动画效果;

底部导航栏 :实现4个页面的切换;

响应式布局:自动适配不同设备尺寸。


五、 总结与未来学习路径 🚀

5.1 总结

本文作为《鸿蒙APP开发从入门到精通》的第2篇,完成了:

  • 对ArkUI组件库分类与核心特性的理解;
  • 15个常用ArkUI组件的实战应用;
  • 一个完整的电商首页组件的创建;
  • 组件交互效果的优化(悬停、点击动画)。

5.2 未来学习路径

  • 第3篇:自定义组件与数据双向绑定;
  • 第4篇:网络请求与数据持久化;
  • 第5篇:页面路由与组件跳转;
  • 第6篇:原子化服务与元服务卡片的开发;
  • 第7篇:超级终端多设备协同开发;
  • 第8篇:服务联邦跨服务无缝打通;
  • 第9篇:安全加固与组件化架构;
  • 第10篇:AI原生与用户增长;
  • 第11篇:性能优化与Next原生合规;
  • 第12篇:运维监控、生态运营与专属变现。

结语 ✅

恭喜你!你已经完成了《鸿蒙APP开发从入门到精通》的第2篇,掌握了ArkUI组件库的核心用法。

从现在开始,你已具备了开发复杂UI界面的能力。未来的10篇文章将逐步构建一个完整的鸿蒙电商购物车全栈项目,并最终实现华为应用市场上架变现。

让我们一起期待鸿蒙生态的爆发! 🎉🎉🎉

相关推荐
摘星编程4 小时前
React Native鸿蒙版:StackNavigation页面返回拦截
react native·react.js·harmonyos
BlackWolfSky4 小时前
鸿蒙中级课程笔记4—应用程序框架进阶1—Stage模型应用组成结构、UIAbility启动模式、启动应用内UIAbility
笔记·华为·harmonyos
Miguo94well5 小时前
Flutter框架跨平台鸿蒙开发——植物养殖APP的开发流程
flutter·华为·harmonyos·鸿蒙
九 龙5 小时前
Flutter框架跨平台鸿蒙开发——电影拍摄知识APP的开发流程
flutter·华为·harmonyos·鸿蒙
九 龙5 小时前
Flutter框架跨平台鸿蒙开发——如何养花APP的开发流程
flutter·华为·harmonyos·鸿蒙
ujainu6 小时前
Flutter + OpenHarmony 开关与选择器:Switch、Checkbox、Radio 与 DropdownButton 的无障碍适配
flutter·组件
摘星编程7 小时前
React Native鸿蒙:Button自定义样式按钮
react native·react.js·harmonyos
HiEV9 小时前
智能驾驶,没有中场战事只有无限战争
华为·辅助驾驶·智能汽车
Easonmax9 小时前
基础入门 React Native 鸿蒙跨平台开发:简单模拟一个温度计
react native·react.js·harmonyos