【ArkUI 练中学】第15课:UI 界面设计与实战

本节目标

· 掌握 ArkUI 常用基础组件(Text、Image、Button、TextInput、List)的核心属性与用法

· 掌握 Column、Row、Flex、Stack 四大布局容器的适用场景与对齐控制方法

· 理解主轴与交叉轴的概念,能够使用 justifyContent 与 alignItems 精准控制布局

· 掌握 stateStyles 多态样式,能够为组件设置正常态、按压态、禁用态等不同状态下的样式

· 理解响应式布局的核心思想,掌握断点系统与媒体查询的基本用法

· 能够综合运用组件、布局与样式,独立完成一个完整的 UI 页面开发

一、ArkUI 组件体系概览

1.1 组件的分类

ArkUI 的组件体系可以按功能分为以下几类:

基础组件:负责展示或交互,如 Text(文本)、Image(图片)、Button(按钮)、TextInput(文本输入框)、Slider(滑动条)等。

容器组件:负责组织子组件的排列方式,如 Column(垂直排列)、Row(水平排列)、Stack(层叠排列)、Flex(弹性布局)、List(列表)、Grid(网格)等。

媒体组件:处理视频、音频等多媒体内容,如 Video、Audio。

导航组件:处理页面间的跳转与导航,如 Navigation、Tabs、Swiper。

1.2 组件的通用属性

所有组件都支持一组通用属性,包括:

· 尺寸:.width()、.height()、.size()

· 边距与内边距:.margin()、.padding()

· 背景:.backgroundColor()、.backgroundImage()

· 边框:.border()、.borderRadius()

· 字体:.fontSize()、.fontColor()、.fontWeight()

· 布局:.layoutWeight()、.align()、.position()

· 多态样式:.stateStyles()

二、常用基础组件详解

2.1 Text 文本组件

Text 组件用于显示一段文本内容,支持富文本、字体样式、文本对齐等属性。

typescript 复制代码
Text('Hello ArkUI')
  .fontSize(24)
  .fontColor('#333333')
  .fontWeight(FontWeight.Bold)
  .textAlign(TextAlign.Center)
  .maxLines(2)
  .textOverflow({ overflow: TextOverflow.Ellipsis })
  .lineHeight(32)
  .letterSpacing(1)

核心属性:fontSize 设置字号,fontColor 设置颜色,fontWeight 设置字重,textAlign 设置水平对齐,maxLines 限制最大行数,textOverflow 设置溢出处理方式(省略号、裁剪等)。

富文本子组件:Text 可以包含 Span 子组件,实现不同样式的文本拼接:

typescript 复制代码
Text() {
  Span('¥').fontSize(14).fontColor(Color.Red)
  Span('99').fontSize(24).fontColor(Color.Red).fontWeight(FontWeight.Bold)
  Span(' 起').fontSize(12).fontColor('#999')
}

2.2 Image 图片组件

Image 组件用于展示图片,支持本地资源、网络图片和 PixelMap。

typescript 复制代码
// 本地资源
Image($r('app.media.app_icon'))
  .width(48)
  .height(48)
  .borderRadius(24)
  .objectFit(ImageFit.Cover)

// 网络图片
Image('https://example.com/avatar.jpg')
  .width(100)
  .height(100)
  .alt($r('app.media.placeholder'))  // 加载失败时的占位图

核心属性:objectFit 控制图片缩放方式(Cover、Contain、Fill、None 等),alt 设置占位图,interpolation 设置插值方式。

2.3 Button 按钮组件

Button 组件是响应用户点击操作的核心交互组件,支持胶囊型、圆形和普通按钮三种类型。

typescript 复制代码
// 胶囊按钮(默认)
Button('确认')
  .type(ButtonType.Capsule)
  .backgroundColor('#007DFF')
  .fontColor(Color.White)
  .width(120)
  .height(40)
  .onClick(() => {
    console.info('按钮被点击')
  })

// 带图标的按钮
Button() {
  Row({ space: 6 }) {
    Image($r('app.media.icon_share')).width(20).height(20)
    Text('分享').fontSize(16).fontColor(Color.White)
  }
}
.type(ButtonType.Capsule)
.backgroundColor('#007DFF')
.padding({ left: 20, right: 20 })

核心属性:type 设置按钮类型(Capsule、Circle、Normal),stateEffect 控制按压效果,onClick 绑定点击事件。

2.4 TextInput 文本输入组件

TextInput 是单行文本输入框,适用于用户名、搜索框等场景。

typescript 复制代码
TextInput({ placeholder: '请输入用户名', text: this.username })
  .width('100%')
  .height(48)
  .fontSize(16)
  .placeholderColor('#999')
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .padding({ left: 16, right: 16 })
  .maxLength(20)
  .type(InputType.Normal)  // Normal / Password / Email / Number
  .onChange((value: string) => {
    this.username = value
  })

核心属性:placeholder 设置提示文字,text 设置初始值,maxLength 限制最大长度,type 设置输入类型,onChange 监听输入变化。

2.5 List 列表组件

List 是 ArkUI 中的列表容器组件,用于呈现连续、多行或多列的同类数据,支持垂直或水平滚动。

typescript 复制代码
List({ space: 12 }) {
  ForEach(this.messages, (msg: Message) => {
    ListItem() {
      Row({ space: 12 }) {
        Image(msg.avatar).width(44).height(44).borderRadius(22)
        Column({ space: 4 }) {
          Text(msg.name).fontSize(16).fontWeight(FontWeight.Medium)
          Text(msg.content).fontSize(14).fontColor('#666').maxLines(1)
        }
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)
      }
      .width('100%')
      .padding(16)
    }
  }, (msg: Message) => msg.id.toString())
}
.width('100%')
.divider({ strokeWidth: 1, color: '#E5E5EA', startMargin: 72 })

核心属性:space 设置列表项间距,divider 设置分割线,scrollBar 控制滚动条显示。List 的子组件必须是 ListItem 或 ListItemGroup。

三、布局容器实战

3.1 布局的基本概念

ArkUI 的布局遵循约束、测量和放置的过程:父组件向子组件传递可用空间和尺寸约束,子组件根据内容计算自身大小,父组件按照布局规则确定子组件的位置。

主轴与交叉轴:每个布局容器都有一个主轴和一个交叉轴。Column 的主轴是垂直方向,交叉轴是水平方向;Row 的主轴是水平方向,交叉轴是垂直方向。

3.2 Column 垂直布局

Column 默认从上到下排列子组件,适合页面骨架、表单和纵向信息区。

typescript 复制代码
Column({ space: 12 }) {
  Text('Lulu').fontSize(22).fontWeight(FontWeight.Bold)
  Text('HarmonyOS 应用开发者').fontSize(14).fontColor('#667085')
  Button('编辑资料')
}
.width('100%')
.padding(20)
.alignItems(HorizontalAlign.Start)
.backgroundColor(Color.White)
.borderRadius(12)

space 用于设置相邻子组件之间的固定间距,比给每个组件分别添加 margin 更简洁。alignItems(HorizontalAlign.Start) 控制子组件在水平方向靠左对齐。

3.3 Row 水平布局

Row 从左到右排列子组件,适合头像与昵称、标题与操作按钮、工具栏等场景。

typescript 复制代码
Row({ space: 12 }) {
  Image($r('app.media.app_icon')).width(48).height(48).borderRadius(24)
  Column({ space: 4 }) {
    Text('张三').fontSize(16).fontWeight(FontWeight.Medium)
    Text('在线').fontSize(13).fontColor('#34C759')
  }
  .alignItems(HorizontalAlign.Start)
  .layoutWeight(1)
  Button('关注')
    .type(ButtonType.Capsule)
    .backgroundColor('#007DFF')
    .height(32)
}
.width('100%')
.padding(16)

3.4 Stack 层叠布局

Stack 容器中的子元素依次入栈,后一个子元素覆盖前一个子元素,子元素可以叠加,也可以设置位置。层叠布局适用于广告、卡片层叠、悬浮按钮等场景。

typescript 复制代码
Stack({ alignContent: Alignment.BottomEnd }) {
  Image($r('app.media.banner'))
    .width('100%')
    .height(200)
    .borderRadius(12)
    .objectFit(ImageFit.Cover)

  Text('限时优惠')
    .fontSize(14)
    .fontColor(Color.White)
    .backgroundColor(Color.Red)
    .padding({ left: 10, right: 10, top: 5, bottom: 5 })
    .borderRadius(4)
    .margin(12)
}
.width('100%')
.height(200)

alignContent 控制子元素的对齐方式,支持九种对齐位置。

3.5 Flex 弹性布局

Flex 布局提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。适用于需要动态伸缩和换行的场景。

typescript 复制代码
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Start }) {
  ForEach(this.tags, (tag: string) => {
    Text(tag)
      .fontSize(14)
      .padding({ left: 12, right: 12, top: 6, bottom: 6 })
      .backgroundColor('#F0F0F0')
      .borderRadius(16)
      .margin({ right: 8, bottom: 8 })
  })
}
.width('100%')

四、多态样式 stateStyles

4.1 什么是多态样式

多态样式(stateStyles)可以依据组件的内部状态的不同,快速设置不同样式,类似于 CSS 伪类,但语法不同。ArkUI 提供六种状态:focused(获焦态)、normal(正常态)、pressed(按压态)、disabled(不可用态)、clicked(点击态)、selected(选中态)。

4.2 基本用法

typescript 复制代码
Button('点击我')
  .stateStyles({
    normal: {
      .backgroundColor('#007DFF')
      .fontColor(Color.White)
    },
    pressed: {
      .backgroundColor('#005BB5')
      .scale({ x: 0.96, y: 0.96 })
    },
    disabled: {
      .backgroundColor('#CCCCCC')
      .fontColor('#999999')
    }
  })
  .enabled(this.isEnabled)
  .onClick(() => {
    // 点击处理
  })

4.3 事件冒泡与阻止

默认情况下,子组件的按压事件会冒泡到父组件,导致父组件的 stateStyles 也被触发。如果希望子组件改变时不影响父组件,可以在子组件绑定 onTouch 回调,并在回调中通过 stopPropagation 方法阻止事件传递。

typescript 复制代码
@Component
struct ChildCard {
  build() {
    Column() {
      Text('子组件内容')
    }
    .stateStyles({
      pressed: {
        .backgroundColor('#F7CE00')
      },
      normal: {
        .backgroundColor('#50BE33')
      }
    })
    .onTouch((event: TouchEvent) => {
      event.stopPropagation()  // 阻止冒泡到父组件
    })
  }
}

五、响应式布局入门

5.1 断点系统

HarmonyOS 提供断点系统用于适配手机、平板、折叠屏、PC 等不同设备形态。常用断点划分如下:

· sm(小屏) :320vp ≤ 宽度 < 520vp,对应手机竖屏

· md(中屏) :520vp ≤ 宽度 < 840vp,对应手机横屏、折叠屏展开态

· lg(大屏) :宽度 ≥ 840vp,对应平板、PC

5.2 媒体查询

使用 mediaquery 模块可以监听窗口尺寸变化,动态调整 UI 布局。

typescript 复制代码
import { mediaquery } from '@kit.ArkUI';

@Entry
@Component
struct ResponsivePage {
  @State currentBreakpoint: string = 'sm';
  private listener: mediaquery.MediaQueryListener | null = null;

  aboutToAppear(): void {
    this.listener = mediaquery.matchMediaSync('(width>=840vp)');
    this.listener.on('change', (result: mediaquery.MediaQueryResult) => {
      this.currentBreakpoint = result.matches ? 'lg' : 'sm';
    });
  }

  aboutToDisappear(): void {
    this.listener?.off('change');
  }

  build() {
    Column() {
      if (this.currentBreakpoint === 'lg') {
        // 大屏布局:分栏展示
        Row() {
          this.sidePanel()
          this.mainContent()
        }
      } else {
        // 小屏布局:单栏展示
        Column() {
          this.mainContent()
        }
      }
    }
  }

  @Builder
  sidePanel() {
    Column().width(240).backgroundColor('#F5F5F5')
  }

  @Builder
  mainContent() {
    Column().layoutWeight(1).backgroundColor(Color.White)
  }
}

六、综合实战:个人资料卡片

以下是一个综合运用组件、布局与样式的完整示例------个人资料卡片页面:

typescript 复制代码
@Entry
@Component
struct ProfileCardPage {
  @State isFollowed: boolean = false;
  @State username: string = 'HarmonyOS 开发者';

  build() {
    Column({ space: 20 }) {
      // 顶部卡片区域
      Column({ space: 16 }) {
        // 头像与基本信息(Row 布局)
        Row({ space: 16 }) {
          Image($r('app.media.avatar'))
            .width(72).height(72).borderRadius(36)
            .border({ width: 3, color: '#007DFF' })

          Column({ space: 6 }) {
            Text(this.username)
              .fontSize(20).fontWeight(FontWeight.Bold)
            Text('关注了 128 人 · 粉丝 1024')
              .fontSize(13).fontColor('#666')
          }
          .alignItems(HorizontalAlign.Start)
          .layoutWeight(1)

          Button(this.isFollowed ? '已关注' : '关注')
            .type(ButtonType.Capsule)
            .backgroundColor(this.isFollowed ? '#E5E5EA' : '#007DFF')
            .fontColor(this.isFollowed ? '#333' : Color.White)
            .height(34)
            .stateStyles({
              pressed: { .scale({ x: 0.95, y: 0.95 }) }
            })
            .onClick(() => {
              this.isFollowed = !this.isFollowed;
            })
        }
        .width('100%')

        // 个人简介(Text 组件)
        Text('热爱 HarmonyOS 开发,专注于 ArkUI 界面与交互设计。')
          .fontSize(14).fontColor('#444').lineHeight(22)
          .maxLines(3).textOverflow({ overflow: TextOverflow.Ellipsis })
      }
      .width('100%')
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .shadow({ radius: 12, color: '#10000000', offsetY: 4 })

      // 标签区域(Flex 换行布局)
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(['ArkUI', 'ArkTS', 'HarmonyOS', 'UI设计', '前端开发'], (tag: string) => {
          Text(tag)
            .fontSize(13)
            .padding({ left: 14, right: 14, top: 6, bottom: 6 })
            .backgroundColor('#F0F5FF')
            .fontColor('#007DFF')
            .borderRadius(16)
            .margin({ right: 8, bottom: 8 })
        })
      }
      .width('100%')

      // 数据统计(Row 等分布局)
      Row() {
        this.statItem('动态', '56')
        Divider().vertical(true).height(32).color('#E5E5EA')
        this.statItem('关注', '128')
        Divider().vertical(true).height(32).color('#E5E5EA')
        this.statItem('粉丝', '1024')
      }
      .width('100%')
      .padding(20)
      .backgroundColor(Color.White)
      .borderRadius(16)
      .justifyContent(FlexAlign.SpaceEvenly)
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .backgroundColor('#F5F7FA')
    .alignItems(HorizontalAlign.Center)
  }

  @Builder
  statItem(label: string, value: string) {
    Column({ space: 6 }) {
      Text(value).fontSize(20).fontWeight(FontWeight.Bold)
      Text(label).fontSize(13).fontColor('#999')
    }
    .layoutWeight(1)
    .alignItems(HorizontalAlign.Center)
  }
}

七、多元化习题

习题 1(判断题)

题目:在 ArkUI 中,Column 容器的主轴方向是水平方向,交叉轴方向是垂直方向。

答案:错误

解读:Column 容器默认从上到下排列子组件,其主轴是垂直方向,交叉轴是水平方向。Row 容器的主轴才是水平方向。

习题 2(单选题)

题目:以下哪个属性用于控制 Image 组件的图片缩放方式?

A. borderRadius

B. objectFit

C. interpolation

D. alt

答案:B

解读:objectFit 控制图片的缩放方式(Cover、Contain、Fill、None 等)。borderRadius 设置圆角,interpolation 设置插值方式,alt 设置加载失败时的占位图。

习题 3(多选题)

题目:关于 stateStyles 多态样式,以下说法正确的有(多选):

A. stateStyles 可以依据组件的内部状态设置不同样式

B. pressed 状态表示组件按压态

C. 子组件的按压事件默认不会冒泡到父组件

D. 可以在子组件中通过 stopPropagation 阻止事件冒泡

答案:A、B、D

解读:stateStyles 可以依据组件的内部状态(focused、normal、pressed、disabled 等)快速设置不同样式,选项 A 正确。pressed 表示组件按压态,选项 B 正确。默认情况下,子组件的按压事件会冒泡到父组件,导致父组件的 stateStyles 也被触发,选项 C 错误。可以在子组件中通过 stopPropagation 阻止事件冒泡,选项 D 正确。

习题 4(代码填空题)

题目:请补全以下代码,实现一个胶囊按钮,点击时缩小到 0.95 倍。

typescript 复制代码
Button('点击我')
  .type(ButtonType.Capsule)
  .backgroundColor('#007DFF')
  .______________({
    pressed: {
      .scale({ x: 0.95, y: 0.95 })
    }
  })

答案:stateStyles

解读:stateStyles 是属性方法,用于根据组件的内部状态设置不同样式。pressed 状态表示组件按压态,在其中设置 .scale({ x: 0.95, y: 0.95 }) 即可实现按压时缩小的效果。

习题 5(代码改错题)

题目:以下代码存在布局问题,请指出问题并修正。

typescript 复制代码
Column() {
  Text('标题').fontSize(20)
  Text('内容').fontSize(14)
  Text('底部').fontSize(12)
}
.height(200)

答案:Column 容器设置了固定高度 200,但三个 Text 组件默认在垂直方向靠上排列,底部区域没有利用起来。如果需要让内容在垂直方向均匀分布,应设置 justifyContent 属性:

typescript 复制代码
Column() {
  Text('标题').fontSize(20)
  Text('内容').fontSize(14)
  Text('底部').fontSize(12)
}
.height(200)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.Center)

解读:当 Column 拥有多余的垂直空间时,可以通过 justifyContent 分配主轴空间。SpaceBetween 使子组件分别贴近容器顶部和底部,并在水平方向居中。

习题 6(简答题)

题目:简述 Column、Row、Stack 三种布局容器的适用场景及核心对齐控制方式。

答案:Column 容器沿垂直方向排列子组件,适合页面骨架、表单、纵向信息区。通过 justifyContent 控制主轴(垂直方向)对齐,通过 alignItems 控制交叉轴(水平方向)对齐。Row 容器沿水平方向排列子组件,适合头像与昵称、标题与操作按钮、工具栏等场景。通过 justifyContent 控制主轴(水平方向)对齐,通过 alignItems 控制交叉轴(垂直方向)对齐。Stack 容器中的子元素依次入栈,后一个覆盖前一个,适用于广告、卡片层叠、悬浮按钮等场景。通过 alignContent 控制子元素在容器中的对齐位置,支持九种对齐方式。

解读:三种布局容器各有侧重,实际开发中常常组合使用。例如用 Column 搭建页面骨架,内部用 Row 排列水平信息,再用 Stack 实现图片上的文字叠加。

八、本节知识点总结

组件体系

ArkUI 组件分为基础组件(Text、Image、Button、TextInput、List 等)、容器组件(Column、Row、Stack、Flex、List、Grid 等)、媒体组件和导航组件。所有组件共享通用属性,包括尺寸、边距、背景、边框、字体、布局和多态样式。

常用基础组件

Text 支持富文本 Span 子组件和溢出处理;Image 通过 objectFit 控制缩放方式;Button 支持 Capsule、Circle、Normal 三种类型;TextInput 通过 onChange 监听输入变化;List 的子组件必须是 ListItem。

布局容器

Column 垂直排列,主轴为垂直方向;Row 水平排列,主轴为水平方向;Stack 层叠排列,通过 alignContent 控制对齐;Flex 弹性布局,支持 FlexWrap.Wrap 自动换行。justifyContent 控制主轴对齐,alignItems 控制交叉轴对齐。

多态样式 stateStyles

根据组件内部状态(normal、pressed、disabled、focused、clicked、selected)设置不同样式。子组件的按压事件默认会冒泡到父组件,可通过 stopPropagation 阻止。

响应式布局

断点系统将设备宽度划分为 sm(<520vp)、md(520-840vp)、lg(≥840vp)三个区间。使用 mediaquery.matchMediaSync 监听窗口尺寸变化,根据断点切换不同布局。

综合实战

一个完整的页面开发需要综合运用组件(Text、Image、Button)、布局(Column、Row、Flex)、样式(圆角、阴影、间距)和多态反馈。合理使用 @Builder 抽取可复用 UI 片段可以提升代码可维护性。

下节预告

第16课将进入 ArkUI 导航与多页面应用的学习,涵盖 Navigation 组件的完整用法、Tab 选项卡与侧边栏布局、以及多页面应用的路由架构设计。

相关推荐
HwJack202 小时前
【HarmonyOS开发小实践】ArkUI 动画系统属性动画、显式动画与路径动画
ui·性能优化·harmonyos
传奇开心果编程3 小时前
【springboot基础语法学与练】第 1 课:从零开始
java·spring boot·后端·学习
扶风ff3 小时前
练题簿小程序:家庭在线练题,课后复习更简单
学习·小程序
传奇开心果编程4 小时前
【Flutter入门练中学】第2课:布局系统
学习·flutter·ui
彧azz4 小时前
Linux 网络编程学习总结
linux·网络·笔记·学习·面试
李游Leo4 小时前
HarmonyOS 7 实战开发 05:把页面做到可上线状态
ios·harmonyos
一尘之中4 小时前
指挥控制中心与指控系统:概念、应用、厂商与DDS技术全景
学习·架构·ai写作
一条破秋裤5 小时前
测试A学习顺序
学习
传奇开心果编程5 小时前
【Jetpack Compose基础语法学与练】第8课 rememberSaveable,页面旋转/系统重建保留状态
android·学习·ui·kotlin·android jetpack