简易钢琴上手体验:ArkTS 页面 UI 与弹奏交互


实例:简易钢琴(Piano)|技术:画布布局、音符提示、静音开关、弹奏体验
一、页面架构总览
Column
├── 标题栏 // 标题 + 音符提示 + 静音开关 + 刷新
├── 钢琴画布 // Canvas:96% 宽 × 210 高,白键+黑键
└── 音阶说明 // C3 D3 E3 F3 G3 A3 B3 提示
页面结构简洁:键盘画布是绝对主体,标题栏承载"当前音符"与"静音开关"。
二、画布与键盘适配
Canvas(this.ctx)
.width('96%')
.height(210)
.borderRadius(12)
.border({ width: 1, color: '#E2E8F0' })
.onReady(() => {
this.canvasW = this.ctx.width;
this.canvasH = this.ctx.height;
this.draw();
})
96% 宽 × 210 高:白键 7 个 × 44px + 边距 ≈ 318px,画布 96% 宽(约 350px)足够容纳。210 高容纳白键 200 + 边距。
注意:白键宽 44px 是逻辑值,画布实际宽度可能更大——键盘左对齐(startX=10)绘制,右侧留白。若要键盘居中/铺满,可动态计算 whiteW = (canvasW - 20) / 7(按画布反推键宽,同 34 的反推 CELL 思路)。
三、音符提示
@State pressedKey: string = '';
@State noteText: string = '触摸琴键弹奏';
// 命中时:
this.pressedKey = WHITE_KEYS[i]; // 或 BLACK_NAMES[i]
this.noteText = `♩ ${WHITE_KEYS[i]}`;
双状态量:pressedKey 驱动画布高亮(绘制判断),noteText 驱动标题栏提示(UI 文本)。一个命中事件更新两个状态——一个管绘制、一个管文案。
抬起复位:
} else if (e.type === TouchType.Up) {
this.pressedKey = '';
this.noteText = '触摸琴键弹奏';
this.draw();
}
Up 时清空 pressedKey(高亮消失)+ 提示复位 + 重绘。
四、静音开关
Text(this.mutedText === '' ? '🔊' : '🔇').fontSize(20).margin({ right: 12 })
.onClick(() => {
this.muted = !this.muted;
this.mutedText = this.muted ? 'muted' : '';
})
🔊/🔇 切换:用 mutedText 状态驱动 emoji 显示(''→🔊,'muted’→🔇)。playNote 内部 if (this.muted) return——静音时只高亮不发音。
为什么用 mutedText 而非直接 muted:@State 才能驱动 UI 刷新,muted 是普通变量不触发重渲染。用状态量包装展示值是 ArkTS 的常见技巧(也可以用 @State muted 直接驱动,此处教学展示"包装"手法)。
五、音频播放器生命周期
private players: Array<audio.AudioPlayer | null> = [];
aboutToDisappear(): void {
for (const p of this.players) {
if (p !== null) {
p.release(); // 释放播放器资源
}
}
this.players = [];
}
播放器数组 + 统一释放:多音符并发需要多个播放器,players 数组管理;页面销毁时全部 release()——资源清理纪律(与 34 停定时器同思想)。
播放器创建时机(注释说明):
// 首次播放某音符时创建:
const player = audio.createAudioPlayer();
player.src = `rawfile:///notes/${name}.wav`;
player.play();
this.players.push(player);
懒创建 + 复用:播放过的播放器存数组,同音符再按需 player.stop() 重置后重播。
六、弹奏体验流程
触摸 Down ──► hitKey
│ ├─ 黑键区域?→ 命中黑键
│ ├─ 白键区域?→ 命中白键
│ ├─ pressedKey + noteText 更新
│ ├─ playNote(发音)
│ └─ draw(高亮)
│
触摸 Move ──► hitKey(滑音,连续触发)
│
触摸 Up ──► pressedKey='' + noteText 复位 + draw
C 大调音阶弹奏:依次按 C3→B3 白键,noteText 逐个显示 ♩ C3 ♩ D3…,画布高亮逐键移动——视觉与提示同步。
七、UI 技术要点对照表
| 技术点 | 实现方式 | 生产价值 |
|---|---|---|
| 键盘画布 | 96% × 210 | 主体突出 |
| 双状态量 | pressedKey + noteText | 绘制/文案分离 |
| 抬起复位 | Up 清空 + 重绘 | 状态归零 |
| 静音开关 | mutedText 驱动 emoji | 演示控制 |
| 播放器数组 | players + release | 资源管理 |
| 懒创建 | 首次播放建播放器 | 按需资源 |
八、本篇小结
本篇完成了简易钢琴的界面与生命周期:键盘画布主体化、双状态量(高亮 + 提示)同步更新、静音开关、播放器数组的懒创建与统一释放。UI 层的核心是"一个命中事件驱动两个 UI 通道"——画布高亮(绘制)与音符提示(文案)由同一个 pressedKey 事件触发,互不干扰。
下一篇《完整代码与运行效果》将给出 PianoPage 全量源码解读、操作剧本演示,以及"乐器类应用"通用模板抽象——适用于鼓垫、竖琴、MIDI 键盘等一切"触摸发音"应用。
九、UI 深度扩展
1. 键盘铺满画布
动态键宽:whiteW = (canvasW - 2*startX) / 7——画布不同大小键盘自动铺满。反推几何参数让布局自适应(34/36 都提过)。
2. 双指和弦
e.touches.length === 2 时两个命中点分别处理——pressedKeys 数组 + 多高亮。多点触控是乐器进阶。
3. 键盘滚动(多八度)
画布显示部分八度,左右滑动切换(视口偏移)。视口滚动扩展键盘范围。
4. 录制回放
录音:notes[] = [{name, time}];回放:定时器按 time 依次 playNote。弹奏记录是教学功能。
5. FAQ
Q1:为什么按下用浅蓝/深蓝而非红/绿?
A:钢琴是乐器非警示,蓝系"按下"语义(非红绿的情绪色)。颜色语义随应用类型变化——乐器用中性色。
Q2:静音后点琴键还有反馈吗?
A:有——高亮与音符提示仍在,只是不发音。视觉反馈与声音解耦,静音仅关声音通道。
Q3:播放器 release 后能复用吗?
A:不能,release 销毁资源。下次播放需重新 createAudioPlayer。release 是终态操作,只在页面销毁时调用。
Q4:滑动弹奏时 Up 会误触发吗?
A:Up 只是清空高亮,不影响弹奏序列。滑动过程中 Move 持续触发命中,Up 结束。状态机简单清晰(Down/Move 弹奏,Up 复位)。
Q5:这个键盘能弹完整曲子吗?
A:一个八度能弹简单旋律(小星星、两只老虎)。复杂曲子需多八度(深度扩展 3)。教学聚焦机制,规模可扩展。
十、下篇预告
最后一篇《完整代码与运行效果》将:给出 PianoPage.ets 完整源码逐段解读;演示 C 大调音阶弹奏、黑键、静音开关的完整操作;抽象"触摸发音乐器"通用模板,并给出鼓垫、MIDI 键盘两个改写场景。
更多推荐

所有评论(0)