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 碰一碰双人绑定流程——从双端靠近到关系建立的完整时序

关系数据库 用户B 手机 NFC 通道 用户A 手机 关系数据库 用户B 手机 NFC 通道 用户A 手机 开启 NFC 监听 靠近(< 4cm) onTagDiscovered 触发 发送 userId + archiveId 接收数据 解析用户信息 创建 relation 记录 回传确认消息 创建 relation 记录 显示"牵手成功"动画 显示"牵手成功"动画

一、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 开发中需要:

  1. 导入 @ohos.nfc.tag 模块读取 Tag
  2. 使用 @ohos.nfc.controller 管理 NFC 开关
  3. 使用 @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 实现:

  1. 碰一碰场景:快速分享(TapSharePage)和关系绑定(BindRelationPage)
  2. 波纹脉冲动画:使用三层嵌套圆实现 NFC 通信等待效果
  3. 三种绑定方式:碰一碰 / 二维码 / ID 输入,覆盖面对面和远程场景
  4. 邀请状态机:select → sent → incoming → success 四步流程
  5. 倒计时管理:setInterval 更新剩余时间,数据持久化到 DB

下一篇文章将介绍 NFC Tag 读写与高级应用

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


参考资源:


七、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" }]
        }
      ]
    }
  ]
}

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

Logo

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

更多推荐