39 NFC 碰一碰绑定:TapSharePage 与牵手交互
39 NFC 碰一碰绑定:TapSharePage 与牵手交互
前言

图:39 NFC 碰一碰绑定:TapSharePage 与牵手交互 运行效果截图(HarmonyOS NEXT)
NFC(Near Field Communication) 是鸿蒙生态中一种重要的短距通信方式。在"鹿鹿·笔迹心理分析"App 中,NFC 碰一碰被设计为双人关系绑定和快速分享的核心交互方式——两台鸿蒙手机靠近即可完成配对。
本文以项目中 [TapSharePage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/TapSharePage.ets) 和 [BindRelationPage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/BindRelationPage.ets) 为例,深入解析 NFC 碰一碰绑定的 UI 设计和业务流程。
鸿蒙官方·NFC 开发指南:developer.huawei.com
项目源码仓库:harmony-app GitHub

图:NFC 碰一碰双人绑定流程——从双端靠近到关系建立的完整时序
一、NFC 绑定场景
1.1 两个碰一碰场景
| 场景 | 触发页面 | 数据传递 | 目标 |
|---|---|---|---|
| 快速分享 | TapSharePage | 笔迹分析报告 | 分享给附近的人 |
| 关系绑定 | BindRelationPage | 关系邀请码 | 建立双人合盘 |
1.2 UI 交互设计
TapSharePage 的 NFC 交互区采用环形波纹动画模拟 NFC 通信过程:
// 三层波纹环 + 中心脉冲圆
Stack() {
// 外层波纹环
Column()
.width(240).height(240).borderRadius(120)
.border({ width: 1, color: `rgba(168,144,122,${this.rippleOpacity})` })
.scale({ x: this.rippleScale, y: this.rippleScale })
// 中层波纹环
Column()
.width(190).height(190).borderRadius(95)
.border({ width: 1, color: `rgba(168,144,122,${this.rippleOpacity + 0.1})` })
.scale({ x: this.rippleScale * 0.9, y: this.rippleScale * 0.9 })
// 中心脉冲圆(渐变色)
Column() {
Text('⊕').fontSize(42).fontColor(Color.White)
}
.width(140).height(140).borderRadius(70)
.linearGradient({ angle: 0, colors: [[AppColors.WARM, 0], [AppColors.PRIMARY, 1]] })
.scale({ x: this.rippleScale * 0.7, y: this.rippleScale * 0.7 })
}
二、脉冲动画设计
2.1 循环脉冲
@State rippleScale: number = 1.0
@State rippleOpacity: number = 0.3
@State pulseActive: boolean = true
aboutToAppear() {
this.startPulseAnimation()
}
private startPulseAnimation(): void {
const animate = () => {
if (!this.pulseActive) return
// 放大 + 淡化
animateTo({ duration: 1500, curve: Curve.EaseInOut }, () => {
this.rippleScale = 1.3
this.rippleOpacity = 0
})
// 复位 + 循环
setTimeout(() => {
animateTo({ duration: 0 }, () => {
this.rippleScale = 1.0
this.rippleOpacity = 0.3
})
setTimeout(() => animate(), 200)
}, 1500)
}
setTimeout(() => animate(), 500)
}
动画时序:
T=0 脉冲开始放大(透明度 0.3→0)
T=1500 放大到最大(scale=1.3),透明度 0
T=1500 瞬间归位(scale=1.0, opacity=0.3)
T=1700 下一轮脉冲开始
... 持续循环
三、关系绑定的三种方式
3.1 BindRelationPage 中的三种邀请方式
[BindRelationPage.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/BindRelationPage.ets) 提供了三种绑定方式:
private readonly methods: InviteMethod[] = [
{
key: 'tap',
icon: '⌘',
title: '碰一碰邀请',
sub: '两台鸿蒙手机背靠背 · 自动配对',
color: '#5B7FB6'
},
{
key: 'qrcode',
icon: '⊞',
title: '扫一扫二维码',
sub: '让 TA 扫描你的关系码',
color: '#A8907A'
},
{
key: 'id',
icon: '#',
title: '输入 TA 的鹿鹿 ID',
sub: '8 位字母数字 · 例 LU 8847',
color: '#9BAEA8'
}
]
| 方式 | 适用场景 | 传输方式 | 安全级别 |
|---|---|---|---|
| 碰一碰邀请 | 面对面 | NFC(近距离) | 高 |
| 扫二维码 | 线上/线下均可 | 摄像头扫描 | 中 |
| 输入鹿鹿 ID | 远程 | 网络传输 | 中 |
3.2 绑定流程状态机
select(选择邀请方式)
│
├──→ tap 碰一碰 ──→ sent(等待接受)
├──→ qrcode ─────→ sent(等待接受)
└──→ id 输入 ────→ sent(等待接受)
│
├── incoming(收到邀请)→ accept → success
└── cancel/revoke → 回到上一页
四、邀请卡 UI
4.1 发送邀请视图
选择邀请方式后,进入 SentView(等待接受状态):
@Builder
SentView() {
Column() {
// 对方头像
Stack() {
Text('茂').fontSize(30).fontColor(Color.White)
}
.width(90).height(90).borderRadius(45)
.linearGradient({ angle: 135, colors: [['#F4D5C5', 0], ['#D9A689', 1]] })
Text('邀请已发送给').fontSize(12).fontColor(AppColors.TEXT_2)
Text('茂 茂').fontSize(16).fontFamily(AppFonts.SERIF).fontColor(AppColors.TEXT)
// 邀请卡
Stack() {
Text('PENDING').fontSize(9).letterSpacing(2)
// ...
// 关系码:L · 8 4 7 · M
Text('L · 8 4 7 · M').fontSize(28).letterSpacing(8)
// 倒计时:还剩 03:48:12
Row() {
Text('⏱').fontSize(11)
Text('还剩 ' + this.timerDisplay + ' · ')
Text('邀请将自动失效')
}
}
}
}
4.2 倒计时实现
@State timerDisplay: string = '03:48:12'
private timerInterval: number = -1
startInvite(methodKey: string): void {
this.step = 'sent'
// 每秒更新倒计时
this.timerInterval = setInterval(() => {
const parts = this.timerDisplay.split(':')
let totalSec = parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2])
if (totalSec > 0) totalSec--
this.timerDisplay = this.formatTimer(totalSec)
}, 1000)
}
4.3 接受邀请
private async acceptInvite(): Promise<void> {
this.clearTimers()
this.step = 'success'
// 写入 DB
const partnerArchiveId = await ArchiveDao.create({
user_id: userId,
name: partnerName,
relation: '伴侣',
// ...
})
await RelationDao.create({
user_id: userId,
partner_archive_id: partnerArchiveId,
bound_method: this.selectedMethod,
// ...
})
}
五、分享内容卡片
5.1 TapSharePage 的分享展示
NFC 分享时展示的内容卡片包括缩略图和信息:
Row() {
// 缩略图(手写风格)
Stack() {
Text('字').fontSize(24).fontFamily(AppFonts.SERIF)
.fontColor(AppColors.PRIMARY_DEEP).rotate({ z: -3 })
}
.width(46).height(46).borderRadius(12)
.linearGradient({ angle: 135, colors: [['#F0E5D2', 0], ['#E5D5BC', 1]] })
// 信息
Column() {
Text(`@给${this.userName} · ${this.personalityType}`)
Text('雷达·情绪·解读·拍摄原图').fontSize(11)
}
}
5.2 分享成功反馈
if (this.shareSuccess) {
Column() {
Text('✅').fontSize(32)
Text('分享成功').fontSize(14).fontWeight(AppFonts.W_BOLD)
Text('对方已收到你的笔迹分析').fontSize(11)
}
}
六、碰一碰交互总结
6.1 完整的用户体验流程
① 用户到达 TapSharePage
↓
② 页面显示脉冲波纹动画(等待 NFC 碰一碰)
↓
③ UI 提示:"靠近对方鸿蒙手机背面"
↓
④ 可选操作:生成二维码 / 超级终端推送
↓
⑤ 分享成功 → 显示成功提示 ✅
6.2 UI 与真实 NFC 的衔接
项目目前使用模拟 UI 交互来演示 NFC 场景的流程。真实 NFC 开发中需要:
- 导入
@ohos.nfc.tag模块读取 Tag - 使用
@ohos.nfc.controller管理 NFC 开关 - 使用
@kit.ConnectedTagKit实现设备间数据传输
八、注意事项与常见问题
8.1 开发注意事项
在实际开发过程中,需特别注意以下几点:
- API 兼容性:部分接口仅在特定 HarmonyOS NEXT 版本中可用,需做版本条件判断
- 权限模型:采用静态声明(module.json5)+ 动态申请(requestPermissionsFromUser)的两阶段授权
- 生命周期:合理使用
aboutToAppear()和aboutToDisappear()管理资源初始化与释放 - 状态同步:跨页面数据通过 AppStorage 共享,组件内状态使用
@State/@Prop/@Link装饰器
8.2 常见错误与解决方案
常见问题快速排查表:
| 问题类型 | 排查方向 | 参考方法 |
|---|---|---|
| 应用崩溃 | 查看 hilog 错误日志 | hilog.error(TAG, "...", e.message) |
| 状态丢失 | 检查 AppStorage 键名拼写 | 统一使用常量管理键名 |
| 动画不流畅 | 避免在 animateTo 回调中执行 I/O | 动画与数据操作分离 |
总结
本文从 TapSharePage 和 BindRelationPage 出发,解析了 NFC 碰一碰绑定的设计思路和 UI 实现:
- 碰一碰场景:快速分享(TapSharePage)和关系绑定(BindRelationPage)
- 波纹脉冲动画:使用三层嵌套圆实现 NFC 通信等待效果
- 三种绑定方式:碰一碰 / 二维码 / ID 输入,覆盖面对面和远程场景
- 邀请状态机:select → sent → incoming → success 四步流程
- 倒计时管理:setInterval 更新剩余时间,数据持久化到 DB
下一篇文章将介绍 NFC Tag 读写与高级应用。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
参考资源:
- NFC 开发概述
- [TapSharePage 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/TapSharePage.ets)
- [BindRelationPage 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/pages/BindRelationPage.ets)
- @ohos.nfc.controller API
- @ohos.nfc.tag API
- 鸿蒙短距通信
- HarmonyOS 开发文档
七、NFC 碰一碰进阶:数据格式设计
7.1 NFC 数据包设计
碰一碰传输的数据需要紧凑可靠,项目中使用 JSON 格式:
// NFC 数据包格式
interface NfcPayload {
type: 'bind_relation' | 'share_report' // 操作类型
userId: string // 发送方用户 ID
archiveId: string // 发送方档案 ID
timestamp: number // 时间戳(防重放)
nickname: string // 显示名称
}
// 序列化为字节数组(NFC 传输格式)
function encodePayload(payload: NfcPayload): number[] {
const json = JSON.stringify(payload)
const encoder = new TextEncoder()
return Array.from(encoder.encode(json))
}
// 反序列化
function decodePayload(bytes: number[]): NfcPayload {
const decoder = new TextDecoder()
const json = decoder.decode(new Uint8Array(bytes))
return JSON.parse(json) as NfcPayload
}
7.2 NFC vs 其他短距通信方式
| 方式 | 距离 | 速度 | 使用体验 | 项目使用 |
|---|---|---|---|---|
| NFC 碰一碰 | < 4cm | 慢 | 极直觉,靠近即触发 | ✅ 绑定关系 |
| Bluetooth | 10m | 中 | 需配对,略繁琐 | ❌ |
| Wi-Fi Direct | 100m+ | 快 | 需搜索设备 | ❌ |
| 二维码扫描 | 视线 | - | 需要截图分享 | ✅ 分享报告 |
7.3 NFC 权限与硬件要求
在 module.json5 中声明 NFC 权限:
{
"requestPermissions": [
{
"name": "ohos.permission.NFC_TAG",
"reason": "$string:nfc_permission_reason"
}
],
"deviceTypes": ["phone"], // 仅手机支持 NFC
"abilities": [
{
"skills": [
{
"actions": ["ohos.nfc.tag.action.TAG_FOUND"],
"uris": [{ "type": "tag-tech/NfcA" }]
}
]
}
]
}
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
更多推荐




所有评论(0)