在这里插入图片描述
在这里插入图片描述

1. 应用概述

电影票(MovieTicket)是一款基于HarmonyOS ArkTS框架开发的娱乐类工具应用,核心功能是模拟电影购票流程,帮助用户选择场次和座位。这款应用虽然界面简洁,但完整地展示了 ArkUI 声明式开发的核心模式,包括列表渲染、状态管理、组件交互等关键知识点。

从技术实现角度来看,电影票应用充分利用了 ArkUI 框架的列表展示能力。通过 @State 装饰器管理电影场次数据,通过 ForEach 组件实现列表渲染,通过 Button 组件实现购票交互。应用的数据结构清晰,展示了如何用 Record 类型定义业务数据模型,以及如何在列表中展示复杂的业务信息。

本技术博客将从应用架构设计、核心代码实现、数据模型设计、UI 布局设计、组件交互设计等多个维度,对这款电影票应用进行全面的技术剖析。通过本文的深入讲解,读者能够掌握 HarmonyOS ArkTS 开发中的核心知识点,特别是列表类应用的设计模式、Record 类型的使用方法、ForEach 循环渲染的技巧,以及购票类应用的前端交互设计。

2. 技术架构分析

2.1 整体架构设计

电影票应用采用了标准的单页面架构,整个应用仅包含一个主页面,通过垂直布局的 Column 容器组织各个功能区块。从代码组织角度来看,应用主要分为以下几个核心部分:页面入口组件(MovieTicket)、通用标题栏组件(CommonTitleBar)、电影场次列表区域。

┌─────────────────────────────────────────────────────────┐
│                   MovieTicket页面                        │
│  ┌─────────────────────────────────────────────────┐    │
│  │              CommonTitleBar组件                  │    │
│  │              (导航栏+标题显示)                    │    │
│  └─────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────┐    │
│  │              页面标题区域                        │    │
│  │              (正在热映)                          │    │
│  └─────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────┐    │
│  │              电影场次列表区域                    │    │
│  │              (List + ForEach + ListItem)         │    │
│  │              每个Item包含:                      │    │
│  │              - 电影名称、时间、影厅              │    │
│  │              - 价格、购票按钮                    │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘

电影票应用的结构特点是简洁高效,所有功能集中在一个页面中完成,没有复杂的导航结构。列表区域占据了页面的主要空间,是用户交互的核心区域。

2.2 模块依赖关系

从依赖关系的角度分析,电影票应用主要依赖以下几个核心模块:

首先是 CommonTitleBar 组件,该组件提供了统一的页面标题栏和返回按钮功能,是整个应用的基础导航组件。

其次是 ArkUI 的基础组件:

  • Column:列布局容器,用于垂直排列子组件
  • Row:行布局容器,用于水平排列子组件
  • Text:文本组件,用于显示电影名称、时间、价格等信息
  • Button:按钮组件,用于触发购票操作
  • List:列表容器组件,用于展示电影场次列表
  • ListItem:列表项组件,对应单个电影场次
  • ForEach:循环渲染组件,用于渲染电影场次列表
  • Blank:空白填充组件,用于调整布局空间
  • TextInput:文本输入组件(本应用未使用)

2.3 数据模型设计

电影票应用的数据模型设计了一个电影场次数组:

@State app_movies: Record<string, string>[] = [
  { 'name': '流浪地球2', 'time': '14:00', 'hall': '1号厅', 'price': '45' },
  { 'name': '满江红', 'time': '16:30', 'hall': '2号厅', 'price': '40' },
  { 'name': '深海', 'time': '19:00', 'hall': '3号厅', 'price': '38' }
];
@State app_selectedMovie: Record<string, string> | null = null;
状态变量 类型 初始值 说明
app_movies Record<string, string>[] 预定义3条数据 电影场次数组
app_selectedMovie Record<string, string> | null null 当前选中的电影场次

电影场次的数据结构采用 Record 类型,每个场次包含以下字段:

Record<string, string> = {
  'name': '电影名称',    // string
  'time': '放映时间',    // string
  'hall': '影厅名称',    // string
  'price': '票价'        // string
}

使用 Record 类型而非自定义接口是 ArkTS 中的常见做法。Record<string, string> 表示键为 string 类型、值为 string 类型的对象,这种灵活的类型定义方式适合业务数据模型的快速定义。

3. 核心代码详解

3.1 页面入口与标题栏

电影票应用的入口点使用了 @Entry 和 @Component 两个装饰器:

@Entry
@Component
struct MovieTicket {
  // 状态变量定义
  @State app_movies: Record<string, string>[] = [...];
  @State app_selectedMovie: Record<string, string> | null = null;

  build() {
    Column() {
      CommonTitleBar({
        app_title: '电影票',
        app_showBack: true
      })
      // 页面内容
    }
  }
}

@Entry 装饰器标记这是应用的入口页面,@Component 装饰器标记这是一个 UI 组件。build() 方法是组件的构建入口,返回组件的 UI 结构。

CommonTitleBar 组件的配置说明了应用的基本信息:

CommonTitleBar({
  app_title: '电影票',
  app_showBack: true
})
参数 说明
app_title ‘电影票’ 页面标题
app_showBack true 是否显示返回按钮

3.2 电影列表渲染机制

ForEach 组件是 ArkUI 中渲染列表的核心组件,电影票应用使用它来渲染电影场次列表:

List() {
  ForEach(this.app_movies, (app_movie: Record<string, string>) => {
    ListItem() {
      Row() {
        // 左侧:电影信息
        Column({ space: 4 }) {
          Text(app_movie['name'])
            .fontSize(18)
            .fontWeight(FontWeight.Medium)
            .fontColor($r('app.color.app_color_text_primary'))
          Row({ space: 8 }) {
            Text(app_movie['time'])
              .fontSize(14)
              .fontColor($r('app.color.app_color_text_secondary'))
            Text(app_movie['hall'])
              .fontSize(14)
              .fontColor($r('app.color.app_color_text_secondary'))
          }
        }

        Blank()

        // 右侧:价格和购票按钮
        Column({ space: 4 }) {
          Text(`¥${app_movie['price']}`)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor($r('app.color.app_color_danger'))
          Button('购票')
            .type(ButtonType.Normal)
            .backgroundColor($r('app.color.app_color_primary'))
            .onClick(() => {
              this.app_selectedMovie = app_movie;
            })
        }
      }
      .width('100%')
      .padding(12)
      .backgroundColor($r('app.color.app_color_white'))
      .borderRadius(8)
    }
  })
}
.width('100%')
.layoutWeight(1)

ForEach 的关键参数:

参数 说明
this.app_movies 要遍历的电影场次数组
app_movie 当前遍历到的电影场次对象
app_index 当前元素的索引(本例未使用)

ForEach 的渲染机制:

  1. 接收电影场次数组作为数据源
  2. 遍历数组每个元素
  3. 为每个元素渲染一个 ListItem
  4. ListItem 内部包含完整的电影信息展示

3.3 列表项布局设计

每个电影场次列表项采用了左右分栏的布局设计:

┌─────────────────────────────────────────────────────────┐
│  电影名称           │         ¥45                       │
│  14:00 1号厅        │         [购票]                    │
└─────────────────────────────────────────────────────────┘

左侧区域展示电影的核心信息,包括电影名称、放映时间和影厅。右侧区域展示价格和购票按钮。中间使用 Blank() 组件填充剩余空间,实现左右分离的效果。

布局代码结构:

Row() {  // 外层水平布局
  Column({ space: 4 }) {  // 左侧信息区
    Text(app_movie['name'])  // 电影名称
    Row({ space: 8 }) {     // 时间和影厅行
      Text(app_movie['time'])
      Text(app_movie['hall'])
    }
  }

  Blank()  // 中间自适应填充

  Column({ space: 4 }) {  // 右侧操作区
    Text(`¥${app_movie['price']}`)  // 价格
    Button('购票')  // 购票按钮
  }
}
.width('100%')
.padding(12)
.backgroundColor($r('app.color.app_color_white'))
.borderRadius(8)

3.4 购票交互实现

购票按钮的交互通过 onClick 事件处理:

Button('购票')
  .type(ButtonType.Normal)
  .backgroundColor($r('app.color.app_color_primary'))
  .onClick(() => {
    this.app_selectedMovie = app_movie;
  })

购票流程分析:

  1. 用户点击购票按钮
  2. onClick 回调触发
  3. 将当前电影场次赋值给 app_selectedMovie 状态变量
  4. UI 自动重新渲染(如果后续添加订单确认页面,可以基于此状态显示)

按钮样式配置说明:

属性 说明
type ButtonType.Normal 普通按钮类型
backgroundColor app_color_primary 使用主题色作为背景

ButtonType.Normal 表示普通按钮,这种类型的按钮有圆角背景,适合作为主要操作按钮。相比之下,ButtonType.Circle 是圆形按钮,ButtonType.Text 是文字按钮。

3.5 页面标题设计

页面标题区域采用了简洁的设计:

Text('正在热映')
  .fontSize(18)
  .fontWeight(FontWeight.Bold)
  .fontColor($r('app.color.app_color_text_primary'))
  .width('100%')

标题样式配置:

属性 说明
fontSize 18 字体大小18vp
fontWeight FontWeight.Bold 加粗字体
fontColor app_color_primary 使用主题色
width ‘100%’ 占满宽度

.fontWeight(FontWeight.Bold) 使用了字体权重枚举,常见的值还有 FontWeight.Normal(正常)、FontWeight.Medium(中等粗细)。

3.6 整体布局结构

电影票应用的 UI 布局采用垂直堆叠的 Column 容器:

build() {
  Column() {
    // 1. 标题栏
    CommonTitleBar({
      app_title: '电影票',
      app_showBack: true
    })

    // 2. 内容区域(垂直排列的Column)
    Column({ space: 16 }) {
      // 2.1 页面标题
      Text('正在热映')

      // 2.2 电影列表
      List() {
        ForEach(...)
      }
      .layoutWeight(1)  // 占据剩余高度
    }
    .width('100%')
    .padding(16)
  }
  .width('100%')
  .height('100%')
  .backgroundColor($r('app.color.app_color_background'))
}

布局层级说明:

层级 组件 作用
第1层 Column 根容器,满屏显示
第2层 CommonTitleBar 标题栏组件
第2层 Column 内容区域容器
第3层 Text 页面标题
第3层 List 电影列表容器

layoutWeight(1) 的作用是让 List 组件占据内容区域的全部剩余空间,实现自适应高度。这种布局方式在移动端开发中非常常见,可以确保列表填满屏幕剩余区域。

4. UI 布局设计

4.1 布局容器选择

电影票应用使用了多种布局容器,每种容器都有其特定的使用场景:

Column(列布局)

  • 用于垂直排列子组件
  • 本应用用于整体页面布局、内容区域布局、信息区域布局
  • space 属性设置子组件之间的间距

Row(行布局)

  • 用于水平排列子组件
  • 本应用用于列表项的水平布局
  • space 属性设置子组件之间的间距

Blank(空白填充)

  • 用于占据剩余空间
  • 本应用用于将购票按钮推到右侧
  • 自动扩展以填满可用空间

4.2 样式属性详解

电影票应用中用到的样式属性:

尺寸相关

.width('100%')    // 宽度100%
.height('100%')   // 高度100%
.padding(12)      // 内边距12vp
.padding(16)      // 内边距16vp
.borderRadius(8)  // 圆角8vp

文本相关

.fontSize(18)                    // 字体大小18
.fontWeight(FontWeight.Bold)     // 加粗
.fontColor($r('app.color.xxx')) // 字体颜色

间距相关

Column({ space: 16 })  // 垂直间距16vp
Row({ space: 8 })      // 水平间距8vp

颜色资源引用

$r('app.color.app_color_text_primary')    // 主文本色
$r('app.color.app_color_text_secondary')   // 次要文本色
$r('app.color.app_color_text_tertiary')   // 第三文本色
$r('app.color.app_color_primary')          // 主题色
$r('app.color.app_color_danger')           // 危险/价格色
$r('app.color.app_color_white')            // 白色
$r('app.color.app_color_background')       // 背景色

4.3 卡片式列表项设计

电影票的列表项采用了卡片式设计:

Row() {
  // 电影信息和购票区域
}
.width('100%')
.padding(12)
.backgroundColor($r('app.color.app_color_white'))
.borderRadius(8)

卡片设计的特点:

  • 白色背景与灰色页面背景形成对比
  • 8vp 圆角提供柔和的视觉效果
  • 12vp 内边距提供舒适的内容空间
  • width(‘100%’) 确保卡片宽度填满列表

4.4 价格展示设计

价格展示是购票类应用的关键信息,需要特别设计:

Text(`¥${app_movie['price']}`)
  .fontSize(18)
  .fontWeight(FontWeight.Bold)
  .fontColor($r('app.color.app_color_danger'))

价格样式要点:

  • 使用危险色/强调色(红色系)突出价格
  • 18号字体配合加粗,确保价格醒目
  • ¥ 符号使用模板字符串拼接

5. ArkTS 核心特性应用

5.1 Record 类型详解

电影票应用使用 Record 类型定义电影场次数据结构:

@State app_movies: Record<string, string>[] = [
  { 'name': '流浪地球2', 'time': '14:00', 'hall': '1号厅', 'price': '45' }
];

Record 类型的特点:

  • 键的类型固定为 string
  • 值的类型固定为 string
  • 适合结构相对固定的数据模型
  • 访问属性使用数组下标语法(obj[‘key’])

访问 Record 数据的示例:

let movieName: string = app_movie['name'];     // 获取电影名称
let movieTime: string = app_movie['time'];    // 获取放映时间
let movieHall: string = app_movie['hall'];    // 获取影厅
let moviePrice: string = app_movie['price'];  // 获取票价

5.2 @State 装饰器使用

@State 是 ArkTS 中用于管理组件状态的装饰器:

@State app_movies: Record<string, string>[] = [...];
@State app_selectedMovie: Record<string, string> | null = null;

@State 的特点:

  • 只能在 @Component 修饰的组件中使用
  • 状态变化自动触发 UI 重新渲染
  • 适合管理组件私有状态
  • 数组类型状态修改需要使用数组方法

5.3 $r 资源引用

$r 是 ArkUI 中引用资源的方式:

.fontColor($r('app.color.app_color_text_primary'))
.backgroundColor($r('app.color.app_color_white'))

$r 的优势:

  • 支持主题切换(浅色/深色)
  • 统一管理颜色等资源
  • 提高代码可维护性

5.4 模板字符串使用

电影票应用中使用了模板字符串拼接价格显示:

Text(`¥${app_movie['price']}`)

模板字符串的语法:

  • 使用反引号(`)包裹字符串
  • ${expression} 嵌入表达式
  • 表达式结果自动转换为字符串

6. 购票类应用开发要点

6.1 列表设计模式

购票类应用的列表设计通常包含以下要素:

要素 说明
核心信息 电影名称、航班号、酒店名等
次要信息 时间、地点、评分等
价格信息 票价、起价等
操作按钮 购票、预订、查看等

6.2 左右分栏布局

电影票应用的列表项采用了左右分栏布局:

┌─────────────────────────────────────────────────────────┐
│                                                         │
│  电影名称           │                                  │
│  时间 影厅          │                          [购票]  │
│                     │                           ¥45   │
│                                                         │
└─────────────────────────────────────────────────────────┘

实现要点:

  • 外层使用 Row 实现水平布局
  • 左侧信息区使用 Column 实现垂直排列
  • 中间使用 Blank 自动填充剩余空间
  • 右侧操作区使用 Column 实现垂直排列

6.3 可复用组件设计

电影票应用的可复用组件包括:

CommonTitleBar:通用标题栏组件,提供了统一的导航和标题显示功能。这个组件在整个应用中被多个页面复用,保证了 UI 的一致性。

6.4 交互反馈设计

购票按钮提供了明确的交互反馈:

Button('购票')
  .type(ButtonType.Normal)
  .backgroundColor($r('app.color.app_color_primary'))

交互反馈要点:

  • 使用主题色背景,表示这是主要操作
  • 普通按钮类型,有足够的点击区域
  • 点击后更新选中状态,提供视觉反馈

7. 扩展与展望

7.1 当前功能总结

电影票应用实现了以下核心功能:

功能模块 实现描述
电影展示 使用 List 和 ForEach 渲染电影场次列表
场次信息 显示电影名称、放映时间、影厅信息
价格显示 使用强调色展示票价
购票操作 点击购票按钮选中场次
状态管理 使用 @State 管理选中状态

7.2 功能扩展方向

基于当前的电影票应用架构,可以进行以下功能扩展:

座位选择功能:这是电影购票的核心功能,可以扩展座位平面图:

@State app_seats: boolean[][] = [];  // 座位状态数组
@State app_selectedSeats: string[] = [];  // 已选座位

// 座位选择逻辑
onClick(() => {
  if (this.app_seats[row][col] === false) {
    this.app_seats[row][col] = true;
    this.app_selectedSeats.push(`${row}-${col}`);
  }
})

电影详情页:点击电影名称可以进入详情页:

Text(app_movie['name'])
  .onClick(() => {
    // 跳转到详情页
  })

筛选功能:支持按时间、影厅筛选:

@State app_filterType: string = '全部';

Row({ space: 16 }) {
  Button('全部')
  Button('上午')
  Button('下午')
  Button('晚上')
}

评论展示:显示其他用户的评分和评论:

interface MovieComment {
  userName: string;
  rating: number;
  comment: string;
}

7.3 数据持久化

当前应用的数据是预定义的静态数据,扩展后可以使用 AppStorage 进行持久化:

aboutToAppear(): void {
  // 页面显示时加载数据
}

aboutToDisappear(): void {
  // 页面消失时保存数据
}

8. 技术要点总结

8.1 核心代码模块

电影票应用的代码结构:

@Entry
@Component
struct MovieTicket {
  @State app_movies: Record<string, string>[] = [
    { 'name': '流浪地球2', 'time': '14:00', 'hall': '1号厅', 'price': '45' },
    { 'name': '满江红', 'time': '16:30', 'hall': '2号厅', 'price': '40' },
    { 'name': '深海', 'time': '19:00', 'hall': '3号厅', 'price': '38' }
  ];
  @State app_selectedMovie: Record<string, string> | null = null;

  build() {
    Column() {
      CommonTitleBar({...})
      Column({ space: 16 }) {
        Text('正在热映')
        List() {
          ForEach(this.app_movies, (app_movie) => {
            ListItem() { ... }
          })
        }
        .layoutWeight(1)
      }
      .padding(16)
    }
    .backgroundColor($r('app.color.app_color_background'))
  }
}

8.2 关键技术点

技术点 在本应用中的体现
@Entry 标记页面入口
@Component 标记UI组件
@State 管理电影列表和选中状态
List组件 电影场次列表容器
ForEach组件 遍历渲染电影列表
Record类型 电影场次数据结构
$r资源引用 引用颜色等资源
模板字符串 拼接价格显示
onClick事件 处理购票点击
layoutWeight 自适应列表高度

8.3 组件层级结构

层级 组件 说明
1 Column 根容器
2 CommonTitleBar 标题栏
2 Column 内容区
3 Text 页面标题
3 List 列表容器
4 ForEach 循环渲染
4 ListItem 列表项
5 Row 列表项内容
6 Column 信息区/操作区
7 Text 文本显示
7 Button 按钮

9. 完整代码结构

import { CommonTitleBar } from '../../components/CommonTitleBar';

@Entry
@Component
struct MovieTicket {
  @State app_movies: Record<string, string>[] = [
    { 'name': '流浪地球2', 'time': '14:00', 'hall': '1号厅', 'price': '45' },
    { 'name': '满江红', 'time': '16:30', 'hall': '2号厅', 'price': '40' },
    { 'name': '深海', 'time': '19:00', 'hall': '3号厅', 'price': '38' }
  ];
  @State app_selectedMovie: Record<string, string> | null = null;

  build() {
    Column() {
      CommonTitleBar({
        app_title: '电影票',
        app_showBack: true
      })

      Column({ space: 16 }) {
        Text('正在热映')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor($r('app.color.app_color_text_primary'))
          .width('100%')

        List() {
          ForEach(this.app_movies, (app_movie: Record<string, string>) => {
            ListItem() {
              Row() {
                Column({ space: 4 }) {
                  Text(app_movie['name'])
                    .fontSize(18)
                    .fontWeight(FontWeight.Medium)
                    .fontColor($r('app.color.app_color_text_primary'))
                  Row({ space: 8 }) {
                    Text(app_movie['time'])
                      .fontSize(14)
                      .fontColor($r('app.color.app_color_text_secondary'))
                    Text(app_movie['hall'])
                      .fontSize(14)
                      .fontColor($r('app.color.app_color_text_secondary'))
                  }
                }

                Blank()

                Column({ space: 4 }) {
                  Text(`¥${app_movie['price']}`)
                    .fontSize(18)
                    .fontWeight(FontWeight.Bold)
                    .fontColor($r('app.color.app_color_danger'))
                  Button('购票')
                    .type(ButtonType.Normal)
                    .backgroundColor($r('app.color.app_color_primary'))
                    .onClick(() => {
                      this.app_selectedMovie = app_movie;
                    })
                }
              }
              .width('100%')
              .padding(12)
              .backgroundColor($r('app.color.app_color_white'))
              .borderRadius(8)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
      }
      .width('100%')
      .padding(16)
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.app_color_background'))
  }
}

10. 结语

电影票应用作为一款模拟购票类应用,展示了 HarmonyOS ArkUI 框架在列表展示和交互设计方面的核心能力。通过 List、ForEach、Button 等组件的组合使用,实现了电影场次列表的完整展示和购票交互功能。

从技术角度来看,应用的核心亮点在于列表渲染和布局设计。ForEach 组件配合 List 组件实现了高效列表渲染,Row 和 Column 的嵌套使用实现了复杂的列表项布局,Blank 组件实现了自适应空间填充。

从应用设计的角度来看,电影票应用遵循了"信息清晰、操作便捷"的设计原则。每个列表项都清晰地展示了电影名称、放映时间、影厅和价格信息,用户可以快速选择场次并完成购票操作。价格使用强调色突出显示,让用户一眼就能看到关键信息。

虽然当前应用功能相对简单,但已经完整展示了 ArkTS 声明式开发的核心理念。通过扩展座位选择、电影详情、筛选功能等,可以逐步演进为一个完整的电影购票应用。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐