鸿蒙ArkTS入门:从0到1,我们来“算一卦”!

哈喽,各位未来的鸿蒙大神们!是不是觉得ArkTS的声明式UI看起来很炫,但又不知道从哪下手?别慌,今天我们就用一个充满“玄学”气息的小Demo——《易经初解》,带你轻松拿捏鸿蒙开发的几个核心知识点。

咱们不讲大道理,只看代码和骚操作。上车,发车!

第一站:万物皆有“模”——定义数据模型

在开始盖房子之前,总得有个图纸,对吧?在代码世界里,这个“图纸”就是我们的数据模型。我们要展示“卦象”,就得先定义一个“卦”长什么样。

这里我们用class关键字来创建一个GuaItem类,它就像一个“卦象”的模板,规定了每个卦象都有ID、名字、符号、简介、图片和分类。

// ------------------------------------------
// 1. 数据模型 (Data Model)
// ------------------------------------------
// 定义一个类来描述“卦”的数据结构
// 替代了原来的 NewsItem
class GuaItem {
  id: number = 0;
  name: string = '';       // 卦名 (如: 乾)
  symbol: string = '';     // 卦象 (如: ䷀)
  description: string = '';// 卦辞简介
  imageUrl: string = '';   // 图片 URL (这里用占位符代替)
  category: string = '';   // 分类 (上经 / 下经)

  constructor(id: number, name: string, symbol: string, description: string, imageUrl: string, category: string) {
    this.id = id;
    this.name = name;
    this.symbol = symbol;
    this.description = description;
    this.imageUrl = imageUrl;
    this.category = category;
  }
}

第二站:造个“小卡片”——封装自定义组件 @Component

有了“图纸”,我们就要开始造“零件”了。在鸿蒙开发中,UI界面就是由一个个“组件”拼起来的。@Component装饰器就像一个“组件制造机”,它告诉编译器:“嘿,我要造一个新类型的UI零件了!”

我们这个GuaCard组件,就是专门用来显示一个卦象信息的“卡片”。

// ------------------------------------------
// 2. 子组件:卦象卡片 (GuaCard)
// ------------------------------------------
// 这是一个自定义组件,用于显示单个卦象的信息
@Component
struct GuaCard {

第三站:“投喂”数据——神奇的 @Prop

光有卡片模板还不行,卡片上显示啥内容呢?总不能写死吧。

这时候,@Prop就登场了。它就像在组件上开了一个“投喂口”,父组件(后面会讲到)可以通过这个口子,把数据(比如“乾卦”的信息)塞给它。

注意看,我们用Image($r(this.gua.imageUrl))来加载本地图片资源,$r是ArkTS加载资源的“咒语”。你还很皮地把卦象符号Text(this.gua.symbol).position({ x: 220, y: 28 })定位到了右上角,这波操作很风骚!

  // 知识点: @Prop
  // @Prop 装饰的变量允许父组件向本组件传递数据。
  // 这是父子组件通信的“单向”数据流。
  @Prop gua: GuaItem

  build() {
    // 垂直布局,卡片内的元素竖着排
    Column({ space: 10 }) {
      // 区域1: 图片
      // Stack 允许组件堆叠,我们把卦象符号(symbol)叠在图片(imageUrl)上
      Stack() {
        Image($r(this.gua.imageUrl))
          .width('100%')
          .height(490)
          .objectFit(ImageFit.Cover) // 图片裁剪以覆盖区域
          .borderRadius(10)
          .backgroundColor('#ECECEC') // 占位图的背景色

        // 叠加在图片左上角的“卦象符号”
        Text(this.gua.symbol)
          .fontSize(60)
          .fontColor(Color.Red)
          .backgroundColor('#00000080') // 半透明黑底
          .padding(5)
          .borderRadius(5)
          .position({ x: 220, y: 28 }) // 定位在右上角
      }

      // 区域2: 卦名 (如: 乾 (qián))
      Text(this.gua.name)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .width('100%')
        .textAlign(TextAlign.Start) // 文本左对齐

      // 区域3: 简介 (卦辞)
      Text(this.gua.description)
        .fontSize(14)
        .fontColor(Color.Gray) // 灰色字体
        .width('100%')
        .maxLines(2) // 最多显示2行
        .textOverflow({ overflow: TextOverflow.Ellipsis }) // 超出部分显示省略号
        .textAlign(TextAlign.Start)
    }
    .padding(12) // 卡片内边距
    .backgroundColor(Color.White) // 卡片背景色
    .borderRadius(12) // 卡片圆角
    .shadow({ radius: 6, color: '#1A000000', offsetX: 0, offsetY: 2 }) // 卡片阴影
    .onClick(() => {
      // 知识点: 点击事件
      // 整个卡片都可以点击,点击时打印日志
      console.info(`你点击了: ${this.gua.name}`);
      // 提示:未来可以在这里使用 router.pushUrl(...) 实现页面跳转
    })
  }
}

第四站:闪亮登场!——页面入口 @Entry

零件造好了,得有个“舞台”来展示它们。@Entry装饰器就是这个“舞台”的入口大门,它告诉鸿蒙系统:“老铁,从这里开始加载页面!”

一个页面(Page)通常只有一个@Entry

// ------------------------------------------
// 3. 主页面 (YiJingDemo)
// ------------------------------------------
// 知识点: @Entry
// @Entry 标记的组件是这个页面的入口
@Entry
@Component
export struct YiJingDemo {

第五站:数据的“魔法”——灵魂核心 @State

来了来了,鸿蒙开发(或者说声明式UI)的灵魂核心——@State

@State装饰的变量,就像是被施了魔法。一旦它的值发生改变,所有用到它的UI都会自动、立刻、马上重新刷新!

我们用它来保存“卦象列表”(guaList)和“当前选中的分类”(selectedCategory)。后面你会看到,当我们点击分类按钮时,只需要改一下selectedCategory的值,下面的列表就会自动筛选,简直不要太爽!

  // --- 3.1 状态数据 (State Data) ---

  // 知识点: @State
  // @State 标记的变量是“状态变量”。
  // 当这个变量的值发生变化时,所有使用到它的UI都会自动重新渲染。

  // 《易经》数据列表
  @State guaList: GuaItem[] = [
    // 注意:请将 imageUrl 替换为你自己的真实图片 URL
    // 这里使用的是占位符图片服务
    new GuaItem(1, '乾 (qián)', '䷀', '元、亨、利、贞。象征天,刚健不息。', 'app.media.qian', '上经'),
    new GuaItem(2, '坤 (kūn)', '䷁', '元、亨、利、牝马之贞。象征地,厚德载物。', 'app.media.kun', '上经'),
    new GuaItem(3, '屯 (zhūn)', '䷂', '元、亨、利、贞。勿用有攸往。象征初生,艰难。', 'app.GuaItemmedia.zhun', '上经'),
    new GuaItem(4, '蒙 (méng)', '䷃', '亨。匪我求童蒙,童蒙求我。象征启蒙,教育。', 'app.media.meng', '上经'),
    new GuaItem(60, '节 (jié)', '䷻', '亨。苦节不可贞。象征节制,适度。', 'app.media.jie', '下经'),
    new GuaItem(61, '中孚 (zhōng fú)', '䷼', '豚鱼吉,利涉大川,利贞。象征诚信,中正。', 'app.media.zhongfu', '下经'),
    new GuaItem(62, '小过 (xiǎo guò)', '䷽', '亨,利贞。可小事,不可大事。飞鸟遗之音,不宜上,宜下,大吉。象征小过错,适度。', 'app.media.xiaoguo', '下经'),
    new GuaItem(63, '既济 (jì jì)', '䷾', '亨,小利贞,初吉终乱。象征完成,成功。', 'app.media.jiji', '下经'),
  ];

  // 当前选中的分类
  @State selectedCategory: string = '全部';

  // 所有的分类(这是一个普通变量,因为它不需要改变)
  categories: string[] = ['全部', '上经', '下经'];

第六站:搭积木——build主函数

每个组件都有一个build()函数,它就是这个组件的“搭建图纸”。我们的主页面YiJingDemobuild函数非常简洁:一个垂直布局Column,里面按顺序放上“标题栏”、“分类筛选”和“卦象列表”。

  // --- 3.2 页面构建 (UI Build) ---
  build() {
    // 页面总布局: 垂直排列
    Column({ space: 0 }) {
      // 3.2.1 顶部标题
      this.buildTitleBar()

      // 3.2.2 分类筛选
      this.buildCategoryFilter()

      // 3.2.3 卦象列表
      this.buildGuaList()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F4F4F4') // 页面背景色
  }

第七站:复用大法好——@Builder封装UI片段

你可能会问,buildTitleBar()这些是啥?它们是“构建函数”,被@Builder装饰器标记。

@Builder就像是帮你把一小块UI(比如标题栏)封装成一个“UI配方”,在build函数里喊一声(this.buildTitleBar()),它就把这块UI给“煮”出来了。这样做能让你的build函数保持清爽,可读性up up!

  // --- 3.3 UI 构建函数 (Builder Functions) ---

  // 知识点: @Builder
  // @Builder 标记的函数用于构建可复用的UI片段,使 build() 主体更清晰。

  // 3.3.1 构建标题栏 (简化版)
  @Builder
  buildTitleBar() {
    Row() {
      Text('易经初解')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.Black)
    }
    .width('100%')
    .height(56) // 标题栏高度
    .padding({ left: 16, right: 16 })
    .backgroundColor(Color.White)
    .justifyContent(FlexAlign.Start) // 标题居左
    .shadow({ radius: 4, color: '#1A000000', offsetY: 1 }) // 底部加一点阴影
  }

第八站:魔法联动(上)—— 循环渲染与状态变更


buildCategoryFilter这里是第一个魔法发生地。

  1. 我们用Scroll实现横向滚动。
  2. ForEach遍历categories数组,把“全部”、“上经”、“下经”都造出来。
  3. 看重点fontColorbackgroundColor会根据this.selectedCategory === category来动态改变。
  4. 看重点onClick事件里,我们执行了this.selectedCategory = category;

当你点击“上经”时,@State变量selectedCategory被改成了“上经”。魔法启动!所有用到selectedCategory的地方都会刷新!

  // 3.3.2 构建分类筛选
  @Builder
  buildCategoryFilter() {
    // Scroll 用于创建可横向滚动区域
    Scroll() {
      Row({ space: 12 }) { // 横向排列,间距12
        // 知识点: ForEach 循环渲染列表
        // 遍历 this.categories 数组
        ForEach(this.categories, (category: string) => {
          Text(category)
            .fontSize(14)
            // 知识点: 动态样式
            // 根据 selectedCategory 是否等于当前 category,来决定文字和背景颜色
            .fontColor(this.selectedCategory === category ? Color.White : Color.Black)
            .backgroundColor(this.selectedCategory === category ? '#007DFF' : '#EAEAEA')
            .padding({ left: 16, right: 16, top: 8, bottom: 8 })
            .borderRadius(20)
            .onClick(() => {
              // 知识点: 点击事件更新 @State 变量
              // 当点击时,更新 selectedCategory 的值
              // @State 变量一变,UI会自动刷新
              this.selectedCategory = category;
            })
        })
      }
      .padding(16) // 整个横向滚动区域的内边距
    }
    .scrollBar(BarState.Off) // 关闭滚动条显示
    .backgroundColor(Color.White)
    .margin({ top: 1 }) // 和标题栏留一点点空隙
  }

第九站:魔法联动(下)—— 列表筛选与数据传递

buildGuaList是第二个魔法发生地。

  1. 我们用List来创建一个可滚动的列表。
  2. 看重点ForEach的数据源不是完整的this.guaList,而是this.guaList.filter(...)
  3. 这个.filter()函数会检查我们刚刚改变的@State变量this.selectedCategory
  4. 如果selectedCategory是“全部”,它就不过滤;如果是“上经”,它就只返回category === '上经'的卦象!
  5. ForEach拿到了筛选后的数据,循环创建ListItem,并在里面放上了我们之前造的GuaCard({ gua: gua }),同时把数据通过@Prop“投喂”了进去。

整个过程全自动!我们只改了一个变量,UI就自动完成了“高亮新分类”+“筛选列表”两个操作!

  // 3.3.3 构建卦象列表
  @Builder
  buildGuaList() {
    // 知识点: List 是一个可滚动的列表容器
    List({ space: 12 }) { // 列表项之间的间距
      // 知识点: ForEach 循环渲染列表
      // .filter(...) 用于根据分类筛选
      ForEach(
        // 筛选逻辑:如果选中的是“全部”,则显示所有;否则只显示匹配分类的
        this.guaList.filter(item =>
        this.selectedCategory === '全部' || item.category === this.selectedCategory
        ),
        // 遍历筛选后的数组
        (gua: GuaItem) => {
          // ListItem 是 List 的列表项
          ListItem() {
            // 使用我们自定义的 GuaCard 组件
            // 知识点: 父组件向子组件传递数据
            // 把遍历到的 gua 对象,通过 @Prop 传递给 GuaCard
            GuaCard({ gua: gua })
          }
        },
        // 优化: 为每一项提供一个唯一的 key (id)
        (gua: GuaItem) => gua.id.toString()
      )
    }
    .width('100%')
    .layoutWeight(1) // 知识点: 占满父组件(Column)的剩余空间
    .padding(16) // 列表的内边距
  }
}

收工总结

恭喜你!你已经掌握了鸿蒙开发的几大金刚:

  1. @Component:造零件。
  2. @Entry:App入口。
  3. @Prop:父传子,“投喂”数据。
  4. @State:灵魂核心,数据一变,UI自动刷新。
  5. @Builder:封装“UI配方”,让代码更整洁。
  6. List/ForEach + .filter():动态列表和筛选的黄金搭档。

是不是感觉鸿蒙开发也没那么“玄学”了?赶紧动手试试吧!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐