鸿蒙 ArkTS 投屏设备发现列表 DataTable 布局实现详解



鸿蒙 ArkTS 投屏设备发现列表 DataTable 布局实现详解
一、引言
1.1 背景与项目概述
随着智能家居和万物互联时代的到来,投屏(Screen Casting / Miracast / DLNA)已经成为现代人日常生活中不可或缺的功能。无论是将手机上的视频投射到客厅电视,还是将会议室的 PPT 投放到投影仪,投屏技术都在深刻地改变着我们与数字内容的交互方式。在鸿蒙(HarmonyOS)生态中,投屏能力更是作为分布式架构的核心场景之一,承担着设备间无缝协同的重要使命。
本文基于一个实际的鸿蒙应用开发项目 MyApplication66,深入剖析如何利用鸿蒙 ArkTS 语言及声明式 UI 框架,实现一个功能完备、视觉精美的投屏设备发现列表。该列表以 DataTable(数据表格)为核心交互形态,完整呈现设备名称、在线状态、信号强度以及操作按钮四大维度的信息,适合作为投屏类应用(如 TV Cast、乐播投屏、华为多屏协同等)的设备搜索与连接页面。
1.2 技术栈概览
本项目的技术栈选择充分体现了鸿蒙生态的现代性与先进性:
- 开发语言:ArkTS(基于 TypeScript 扩展的鸿蒙原生声明式语言)
- UI 框架:ArkUI(方舟 UI 框架,提供声明式组件化开发范式)
- 构建工具:Hvigor 6.26.1(鸿蒙新一代轻量级构建系统)
- 目标设备:鸿蒙手机(Phone)
- IDE:DevEco Studio(鸿蒙官方集成开发环境)
1.3 文章结构
本文将从需求分析入手,逐步拆解 DataTable 的每一层实现:从数据模型定义、枚举类型设计,到组件树的构建、样式体系规划,再到交互逻辑的串联。每一部分都将配合核心代码片段进行分析,帮助读者建立起鸿蒙 ArkTS 页面开发从 0 到 1 的完整认知。
二、需求分析与设计思路
2.1 投屏设备列表的核心需求
一个典型的投屏设备发现页面,需要满足以下用户场景:
- 设备发现:用户打开应用后,自动或手动搜索局域网内可投屏的设备
- 信息展示:以列表或表格形式清晰展示每一台设备的名称、类型、当前连接状态和信号质量
- 快速筛选:支持按设备名称或 IP 地址搜索,以及按在线/离线状态分类查看
- 一键连接:用户点击操作按钮即可发起投屏连接,连接过程有明确的视觉反馈
- 状态联动:设备状态变化(在线→使用中、离线恢复在线等)能够实时反映在 UI 上
2.2 DataTable 设计决策
为什么选择 DataTable 而非传统的 Card 列表或 Grid 网格?
| 布局方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| DataTable | 信息密度高、列对齐清晰、便于扫描对比 | 视觉略显紧凑 | 设备信息展示、管理后台 |
| Card 列表 | 视觉宽松、每项独立性强 | 空间利用率低、对比不便 | 内容推荐、社交动态 |
| Grid 网格 | 视觉整齐、适合均等项 | 列数不灵活、信息承载有限 | 图标启动器、相册 |
对于投屏设备列表这一场景,用户需要快速对比多台设备的名称、状态、信号强度三个维度信息,并快速定位到操作按钮。DataTable 的四列布局天然满足这一需求,配合斑马纹(Zebra Striping)的行背景交替设计,阅读效率显著高于其他方案。
2.3 信息架构
页面信息架构由五个层级组成:
- 顶层操作区:标题栏(应用标识 + 页面标题)+ 搜索触发按钮
- 搜索过滤区:文本输入框 + 清除按钮 + 在线/离线筛选标签
- 数据展示区:表头行(列名标识)+ 数据行列表(核心内容)
- 状态反馈区:底部统计栏(在线/离线计数)
- 全局通知区:Toast 浮层(操作结果反馈)
这种信息架构遵循了 F 型视觉动线(用户从上到下、从左到右扫描内容的自然模式),将最关键的设备信息置于视觉热区。
三、数据模型层实现
3.1 枚举类型设计
良好的枚举设计是类型安全的基础。在投屏设备场景中,我们定义了两个核心枚举:
设备类型枚举 DeviceType
enum DeviceType {
TV = 'TV', // 电视
PROJECTOR = 'PROJECTOR', // 投影仪
DONGLE = 'DONGLE', // 投屏器(如 Chromecast、电视果)
STREAMER = 'STREAMER', // 流媒体播放器(如 Apple TV、Shield TV)
AUDIO = 'AUDIO' // 音响设备(如回音壁、智能音箱)
}
这五类设备覆盖了当前市面上绝大多数投屏终端的形态。值得说明的是,将设备类型作为枚举而非字符串常量有三大好处:
- 编译期检查:避免了字符串拼写错误导致的数据异常
- IDE 智能提示:在 DevEco Studio 中输入设备类型时有自动补全
- 可扩展性:新增设备类型只需在枚举中添加一项,所有 switch-case 分支会触发编译警告,提示开发者同步更新
设备状态枚举 DeviceStatus
enum DeviceStatus {
ONLINE = 'ONLINE', // 在线 - 可投屏
OFFLINE = 'OFFLINE', // 离线 - 不可连接
BUSY = 'BUSY' // 使用中 - 已被其他设备占用
}
三个状态的设计并非随意之举。实际的投屏协议(如 Miracast、AirPlay、DLNA)中,设备通常处于以下状态之一:
- 在线(Available/Idle):设备空闲,可接受投屏请求
- 离线(Unreachable/Offline):设备不在线或响应超时
- 使用中(Busy/Streaming):设备正在处理其他投屏会话
引入 BUSY 状态可以有效避免多个用户同时连接同一台设备导致的冲突。
3.2 数据接口设计
interface DeviceInfo {
id: number; // 唯一标识
name: string; // 设备名称(如"客厅 小米电视 S Pro")
type: DeviceType; // 设备类型枚举
status: DeviceStatus; // 当前连接状态
signalStrength: number; // 信号强度(0-100)
ip: string; // 局域网 IP 地址
lastSeen: string; // 上次发现时间
}
该接口的设计遵循 SOLID 原则中的单一职责原则——DeviceInfo 只负责描述设备本身的信息,不包含任何 UI 相关的状态(如"是否正在连接")。UI 状态由组件的 @State 变量单独管理,实现了数据层与表现层的清晰分离。
3.3 模拟数据构造
为演示完整的 DataTable 效果,项目内置了 10 台模拟设备:
@State private devices: DeviceInfo[] = [
{ id: 1, name: '客厅 小米电视 S Pro', type: DeviceType.TV, status: DeviceStatus.ONLINE, signalStrength: 95, ... },
{ id: 2, name: '卧室 华为智慧屏 V75', type: DeviceType.TV, status: DeviceStatus.ONLINE, signalStrength: 82, ... },
{ id: 3, name: '会议室 BenQ 投影仪', type: DeviceType.PROJECTOR, status: DeviceStatus.ONLINE, signalStrength: 60, ... },
// ... 共 10 台
];
模拟数据的设计力求真实:涵盖了不同品牌(小米、华为、三星、索尼、海信等)、不同设备形态(电视、投影仪、投屏器、流媒体盒子、音响)、不同信号强度(从 95 到 0)、不同状态(6 台在线、2 台离线、1 台使用中),以及多样化的 IP 地址段和最后发现时间。
四、UI 组件树的构建
4.1 整体布局结构
ArkUI 的声明式 UI 通过组件嵌套形成组件树。本页面的顶层结构如下:
Stack ← 最外层堆叠容器,用于浮动 Toast
└── Column ← 主内容垂直布局
├── Row ← 标题栏(图标 + 标题 + 搜索按钮)
├── Row ← 搜索栏(输入框 + 清除按钮)
├── Row ← 设备数量提示
├── Row ← 表格标题行(DataTable Header)
├── Divider ← 表头分隔线
├── List ← 表格数据行(DataTable Body)
│ └── ListItem × N ← 每行对应一台设备
│ └── Column
│ ├── Row ← 设备行内容
│ └── Divider ← 行间分隔线
├── Column(if) ← 无结果占位(搜索无匹配时)
└── Row ← 底部统计栏
└── Column(if showToast) ← Toast 通知浮层
这种垂直堆叠的布局方案是移动端列表页面的经典范式,具有以下优点:
- 主滚动方向单一(垂直):用户操作直觉性强
- 各区域职责清晰:标题栏、搜索区、数据区、状态区各司其职
- 条件渲染自然:ArkUI 的
if条件语句可以直接嵌入组件树,控制占位页和 Toast 的显隐
4.2 标题栏实现
标题栏采用一行三列的布局:左侧是图标和标题文字组合,右侧是功能按钮:
Row() {
// 左侧图标
Text('📺')
.fontSize(28)
// 标题区域
Column({ space: 2 }) {
Text('投屏设备').fontSize(...).fontWeight(FontWeight.Bold)
Text('发现并连接附近的投屏设备').fontSize(...)
}
// 弹性空白
Blank()
// 右侧搜索按钮
Button({ type: ButtonType.Circle }) { Text('🔍').fontSize(20) }
.width(44).height(44)
.onClick(() => this.searchDevices())
}
设计要点:
Blank()组件:自动占据剩余空间,将标题和按钮推至两端,实现"两端对齐"效果,比固定 margin 更灵活适配不同屏幕宽度ButtonType.Circle:圆型按钮视觉上更柔和,44×44 的尺寸符合人机交互的最小触控目标(Fitts 定律推荐 ≥ 44pt)- Emoji 作为图标:在原型阶段使用 Emoji 作为占位图标,可以快速验证布局而无需导入矢量图标资源
4.3 搜索栏实现
搜索栏包含输入框和条件清除按钮:
Row() {
TextInput({
placeholder: '搜索设备名称或 IP 地址...',
text: this.searchText
})
.height(44)
.backgroundColor($r('app.color.search_background'))
.borderRadius($r('app.float.search_radius'))
.onChange((val: string) => { this.searchText = val; })
// 有输入内容时显示清除按钮
if (this.searchText !== '') {
Button({ type: ButtonType.Circle }) {
Text('✕').fontSize(14)
}
.width(28).height(28)
.onClick(() => { this.searchText = ''; })
}
}
$r() 是 ArkUI 中引用资源文件的语法。通过将颜色和尺寸值定义在 color.json 和 float.json 资源文件中,我们实现了设计 Token 的统一管理——当 UI 设计规范更新时,只需修改资源文件,所有引用该 Token 的组件会自动同步更新。
4.4 DataTable 表头实现
表头是 DataTable 区别于普通 List 的核心特征之一:
Row() {
Text('设备名称').layoutWeight(3)
Text('状态').layoutWeight(1.2).textAlign(TextAlign.Center)
Text('信号强度').layoutWeight(1.5).textAlign(TextAlign.Center)
Text('操作').layoutWeight(1.5).textAlign(TextAlign.Center)
}
.width('100%')
.padding({ left: 20, right: 16, top: 8, bottom: 8 })
.backgroundColor($r('app.color.card_background'))
.borderRadius({ topLeft: 12, topRight: 12 })
layoutWeight 的妙用:layoutWeight 是 ArkUI 中实现弹性布局的关键属性,它将 Row 的可用宽度按比例分配给子组件。通过设置 3 : 1.2 : 1.5 : 1.5 的比例,我们实现了:
- 设备名称列(权重 3)获得最大空间,适合容纳较长的设备名称
- 状态列(权重 1.2)空间最小,仅为"在线/离线/使用中"几个字
- 信号强度列(权重 1.5)容纳信号图标和文字
- 操作列(权重 1.5)容纳按钮
这种比例分配直接反映了各列的信息承载量,避免了固定宽度在不同屏幕尺寸下的适配问题。
4.5 DataTable 数据行实现
数据行是页面中最复杂的组件,需要呈现多个维度的信息:
ListItem() {
Column() {
Row() {
// ---- 设备名称列 ----
Row({ space: 10 }) {
Text(this.getDeviceIcon(device.type)).fontSize(24)
Column({ space: 2 }) {
Text(device.name).maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 6 }) {
// 设备类型标签
Text(this.getDeviceTypeLabel(device.type))
.fontSize(10)
.border({ width: 0.5, ... })
// IP 地址
Text(device.ip).fontSize(10)
}
}
}
.layoutWeight(3)
// ---- 状态列 ----
Row({ space: 4 }) {
Column().width(8).height(8).borderRadius(4) // 状态圆点
.backgroundColor(this.getStatusColor(device.status))
Text(this.getStatusLabel(device.status))
}.layoutWeight(1.2)
// ---- 信号强度列 ----
Column({ space: 2 }) {
this.renderSignalIndicator(device.signalStrength)
Text(this.getSignalLabel(device.signalStrength))
}.layoutWeight(1.5)
// ---- 操作列 ----
Button() { ... }.layoutWeight(1.5)
}
// 行间分隔线(最后一行不显示)
if (index < this.filteredDevices.length - 1) {
Divider().height(0.5)
}
}
}
数据行的设计体现了三个关键原则:
- 信息层级清晰:一行中通过字号(16fp 设备名 / 10fp 标签和 IP)、字重(Medium / Normal)、颜色(primary / secondary)三个维度建立视觉层级
- 溢出处理:设备名称使用
maxLines(1)+textOverflow(Ellipsis)保证长名不破坏布局 - 分隔线逻辑:最后一行不显示分隔线,避免底部出现多余横线影响整体感
五、信号强度可视化组件
5.1 信号强度柱状图
信号强度的可视化是本页面的一个亮点。不同于简单的文字描述,我们使用 5 根高度递增的柱状条直观展示信号质量:
@Builder
private renderSignalIndicator(strength: number) {
Row({ space: 3 }) {
ForEach([1, 2, 3, 4, 5], (bar: number) => {
Column()
.width(6)
.height(4 + bar * 4) // 高度逐格递增:8,12,16,20,24
.borderRadius(3)
.backgroundColor(
strength === 0
? $r('app.color.divider_color') // 离线:全灰色
: bar * 20 <= strength // 有信号时根据阈值染色
? this.getSignalColor(strength)
: $r('app.color.divider_color')
)
})
}
.alignItems(VerticalAlign.Bottom)
}
设计思路:
- 5 格 Wi-Fi 风格:借鉴了 Wi-Fi 信号图标的设计语言(1 格弱 → 5 格强),用户无需学习即可理解
- 高度递进:
4 + bar * 4使柱高分别为 8、12、16、20、24 vp,形成自然的斜坡轮廓 - 三级色体系:
- ≥ 70:绿色(#34C759),对应"强"
- 40-69:橙色(#FF9500),对应"中"
- < 40:红色(#FF3B30),对应"弱"
- 0:全部灰色,对应"无信号"
这种色彩分级的科学依据是人眼对绿/橙/红的感知差异最为敏感,能够在短时间扫视中快速定位信号质量异常的设备。
5.2 状态指示灯
状态指示采用"圆点 + 文字"的组合形式:
Column()
.width(8).height(8).borderRadius(4)
.backgroundColor(this.getStatusColor(device.status))
三色圆点规则:
| 状态 | 颜色值 | 色名 | 代表含义 |
|---|---|---|---|
| ONLINE | #34C759 | 绿色 | 可用、安全、正常 |
| OFFLINE | #AEAEB2 | 灰色 | 不可用、停用 |
| BUSY | #FF9500 | 橙色 | 忙碌、等待、占用 |
颜色选择遵循了通用设计(Universal Design) 原则——即使在不借助文字描述的情况下,用户也能通过颜色感知设备状态,同时配色也考虑了红绿色盲用户的辨识度(绿色 vs 灰色 vs 橙色的明度差异足够大)。
六、交互逻辑实现
6.1 实时搜索过滤
searchText 作为 @State 变量,每输入一个字符都触发 UI 重渲染:
private get filteredDevices(): DeviceInfo[] {
const text = this.searchText.trim().toLowerCase();
if (text === '') return this.devices;
return this.devices.filter(device =>
device.name.toLowerCase().includes(text) ||
device.ip.includes(text) ||
device.type.toLowerCase().includes(text)
);
}
计算属性(getter)的优势在于:
- 它是纯函数——只要
searchText和devices不变,结果一定相同 - 它没有副作用——不修改任何状态,仅做计算
- 它与 ArkUI 的响应式系统自然集成——当
@State变量改变时,所有引用该 getter 的组件自动重渲染
6.2 连接操作与反馈
连接操作包含完整的用户反馈闭环:
private connectDevice(device: DeviceInfo): void {
// 步骤 1:前置条件检查
if (device.status === DeviceStatus.OFFLINE) { ... return; }
if (device.status === DeviceStatus.BUSY) { ... return; }
// 步骤 2:设置连接中的设备 ID(触发 UI 显示 Loading)
this.connectingDeviceId = device.id;
// 步骤 3:模拟连接(实际项目为网络请求)
setTimeout(() => {
this.connectingDeviceId = null;
this.showToastMessage(`✅ 已成功连接 ${device.name}`);
// 步骤 4:更新设备状态
const index = this.devices.findIndex(d => d.id === device.id);
if (index !== -1) {
this.devices[index].status = DeviceStatus.BUSY;
}
}, 2000);
}
交互反馈闭环:
用户点击 [投屏] → 按钮显示 Loading 动画(2s)→ Toast 提示"连接成功"
→ 按钮文字变为"使用中"(状态更新)
→ 后台自动更新设备列表状态
这个闭环遵循了 Norman 的交互设计七大原则中的即时反馈(Immediate Feedback)——每一次用户操作都有明确的、即时的视觉确认。
6.3 搜索设备模拟
private searchDevices(): void {
this.isSearching = true;
this.searchText = '';
this.showToastMessage('🔄 正在搜索局域网中的投屏设备...');
setTimeout(() => {
this.isSearching = false;
this.showToastMessage(`✅ 搜索完成,发现 ${this.devices.length} 台设备`);
}, 3000);
}
在真实的投屏应用中,这里会调用 @ohos.net.wifi 或自定义的 UDP 广播协议来发现局域网设备。本文使用 setTimeout 模拟异步操作,在原型阶段足以验证 UI 流程的完整性。
6.4 Toast 通知系统
Toast 组件使用条件渲染和过渡动画:
if (this.showToast) {
Column() {
Text(this.toastMessage)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
}
.backgroundColor(Color.Black).opacity(0.85)
.borderRadius(12)
.position({ x: '10%', y: '85%' })
.width('80%')
.transition(
TransitionEffect.opacity(0)
.combine(TransitionEffect.translate({ y: 50 }))
)
.animation({ duration: 300, curve: Curve.Friction })
}
TransitionEffect.opacity(0):进入时从完全透明渐变到不透明TransitionEffect.translate({ y: 50 }):进入时从下方 50vp 位置滑入Curve.Friction:摩擦力曲线让动画结束时带有自然刹车的物理感
七、资源与样式体系
7.1 资源文件架构
鸿蒙应用将颜色、尺寸等样式值统一管理在 element 资源目录下:
entry/src/main/resources/
base/
element/
color.json ← 所有颜色 Token
float.json ← 所有尺寸/字号 Token
string.json ← 字符串资源
dark/
element/
color.json ← 深色模式颜色覆盖
7.2 颜色 Token 设计
在 color.json 中定义了 14 个语义化颜色 Token:
{
"color": [
{ "name": "primary_blue", "value": "#007AFF" },
{ "name": "background_light", "value": "#F5F7FA" },
{ "name": "text_primary", "value": "#1A1A2E" },
{ "name": "text_secondary", "value": "#6B7280" },
{ "name": "status_online", "value": "#34C759" },
{ "name": "status_offline", "value": "#AEAEB2" },
{ "name": "signal_strong", "value": "#34C759" },
{ "name": "signal_medium", "value": "#FF9500" },
{ "name": "signal_weak", "value": "#FF3B30" },
// ...
]
}
语义化命名的好处是:如果未来品牌色从蓝色(#007AFF)更换为紫色(#5856D6),仅需修改 primary_blue 一处,所有引用该 Token 的按钮、链接、标签颜色自动更新。
7.3 尺寸 Token 设计
{
"float": [
{ "name": "title_font_size", "value": "24fp" },
{ "name": "body_font_size", "value": "16fp" },
{ "name": "small_font_size", "value": "13fp" },
{ "name": "card_radius", "value": "16vp" },
{ "name": "button_radius", "value": "20vp" },
{ "name": "list_item_height", "value": "72vp" },
// ...
]
}
fp(Font Pixel)是鸿蒙中的字体像素单位,会跟随系统字体缩放设置自适应;vp(Virtual Pixel)是虚拟像素单位,在不同密度屏幕上保持一致的物理尺寸。
八、最佳实践与设计模式
8.1 组件化思想
虽然本页面所有代码位于一个 Index.ets 文件中,但内部采用了组件化的思维:
| 逻辑单元 | 实现方式 | 复用潜力 |
|---|---|---|
| 信号强度指示器 | @Builder renderSignalIndicator |
高——可在其他页面直接调用 |
| Toast 通知 | showToastMessage + if 条件渲染 |
高——可提取为全局通用组件 |
| 设备行渲染 | ListItem + ForEach |
中——依赖具体数据模型 |
在实际项目中,这些单元应该进一步拆分为独立组件文件,并使用 @Component 装饰器导出。
8.2 响应式状态管理
页面定义了 5 个 @State 变量,分别管理不同维度的状态:
| 变量 | 类型 | 作用域 | 触发重渲染的时机 |
|---|---|---|---|
devices |
DeviceInfo[] | 全部设备数据 | 连接成功后更新状态 |
searchText |
string | 搜索关键字 | 每次键盘输入 |
isSearching |
boolean | 搜索过程 | 开始/结束搜索 |
connectingDeviceId |
number | null | 当前连接 | 点击连接/连接完成 |
showToast |
boolean | Toast 显隐 | 显示/3秒后自动隐藏 |
状态粒度的控制原则:尽可能细粒度。例如,用 connectingDeviceId 代替一个布尔值 isConnecting,这样可以在多台设备同时操作(理论上)时精确控制每一行按钮的状态。
8.3 性能优化
ForEach的键值生成器:(device: DeviceInfo) => device.id.toString(),使用稳定的设备 ID 作为键值,保证列表 diff 更新的效率- 计算属性缓存:
filteredDevices在@State变量不变时不会重新计算 - 条件渲染:Toast 和空状态占位使用
if条件渲染,不占用组件树节点 layoutWeight代替固定宽度:避免多次布局计算
九、与同类方案的比较
9.1 Flutter DataTable vs 鸿蒙 DataTable
| 维度 | Flutter DataTable | 鸿蒙 ArkUI DataTable 实现 |
|---|---|---|
| 原生组件 | 提供 DataTable、DataColumn、DataRow |
无原生 DataTable,使用 List + Row 模拟 |
| 自定义程度 | 高,可配置排序、选择等 | 极高,完全自定义列宽/样式/交互 |
| 开发效率 | 开箱即用 | 需手动组装 |
| 适配灵活性 | 固定列结构 | layoutWeight 弹性分配,天然适配不同屏幕 |
9.2 SwiftUI List vs 鸿蒙 DataTable
SwiftUI 的 List 同样不提供原生 DataTable,但其 Grid 布局(iOS 16+)可以实现类似效果。鸿蒙 List + Row 的组合在灵活性上与 SwiftUI 相当,但在声明式语法的简洁度上略有差异。
十、总结与展望
10.1 本文要点回顾
本文从零到一实现了鸿蒙 ArkTS 投屏设备 DataTable 页面,覆盖了以下关键技术点:
- 数据层:枚举类型(
DeviceType、DeviceStatus)和数据接口(DeviceInfo)的设计 - 布局层:
Stack、Column、Row、List、ForEach等 ArkUI 核心组件的组合使用 - 样式层:语义化资源 Token 系统、
layoutWeight弹性布局、斑马纹列表 - 交互层:实时搜索过滤、连接操作反馈、Toast 通知系统
- 可视化:信号强度柱状图、状态指示灯、条件动画过渡
10.2 后续可扩展方向
- 真实网络发现:接入
@ohos.net.wifi或 MDNS/DLNA 协议,替代模拟数据 - 设备排序:按信号强度降序或设备名称升序排列
- 拖拽排序:用户可自定义设备顺序
- 上下文菜单:长按设备行弹出更多操作(重命名、收藏、查看详情)
- 深色模式适配:利用已定义的
dark/element/color.json自动切换 - 多语言支持:利用
string.json进行国际化
10.3 对鸿蒙开发者的建议
- 善用资源文件:颜色、尺寸、字符串尽量使用
$r()引用资源,而非硬编码 - 组件拆分的时机:当一个
@Builder或内部组件超过 30 行代码时,考虑拆分为独立组件 - 状态管理为先:开发前先梳理清楚数据流和状态变化图,再动手写 UI
- 善用 DevEco Studio:预览器(Previewer)提供了实时布局预览,比反复构建到真机高效得多
附录 A:完整代码
项目完整代码位于 entry/src/main/ets/pages/Index.ets,本文所有代码片段均提取自该文件。
附录 B:资源文件
颜色定义:entry/src/main/resources/base/element/color.json
尺寸定义:entry/src/main/resources/base/element/float.json
更多推荐



所有评论(0)