
本文基于 HarmonyOS 7(API 26)Beta2 公开能力说明与官方课程资料。碰一碰精准分享、普通服务卡片和快游戏互动卡片属于不同能力形态,具体接口、设备范围和上架规则请以当前 SDK 文档为准。
HarmonyOS 7 继续缩短"找到 App---打开页面---选择功能---完成任务"的路径。碰一碰精准分享解决的是内容应该进入哪个设备、哪个窗口、哪个位置;互动卡片解决的是用户能否在轻入口里读懂状态并完成一个短动作。两者都强调效率,但工程边界完全不同。
本文以"手机相册中的图片碰到电脑编辑器画布,随后通过卡片查看处理进度"为例,拆解目标上下文、坐标映射、幂等写入、撤销和卡片状态一致性。
一、普通分享与精准分享差在哪里
普通分享通常只知道目标应用,接收端再让用户选择文档或位置。精准分享进一步携带目标设备、窗口与触碰坐标,使内容可以直接进入当前业务区域。
接收端必须回答三个问题:目标窗口是否仍存在,触碰点属于画布还是只读区域,图片是否满足格式与权限要求。不能把"当前活跃窗口"当作发送时识别到的窗口,否则用户切换文档后可能把素材插错位置。
二、建立稳定的分享上下文
ts
interface PrecisionShareContext {
transferId: string
sourceDeviceId: string
targetDeviceId: string
targetWindowId: string
screenPoint: { x: number; y: number }
content: {
uri: string
mimeType: string
size: number
}
sentAt: number
}
这是业务层示例,不是官方接口原样复制。transferId 用于去重,targetWindowId 用于稳定定位,坐标决定插入位置,内容元数据用于执行门禁。任何字段缺失都应进入明确的恢复流程,而不是猜测默认值。
三、接收流程固定为六步
推荐流程是:解析目标窗口---映射业务区域---校验内容与权限---生成轻量预览---提交可撤销事务---返回结构化结果。
每一步都要有独立错误码:窗口关闭、文档改变、区域只读、格式不支持、文件过大、权限拒绝、存储空间不足。用户看到的提示可以简短,但日志必须能定位失败发生在哪一层。

四、坐标需要完整转换
屏幕坐标不能直接当作画布坐标,还要考虑窗口偏移、标题栏、安全区域、画布滚动、缩放、旋转和显示器缩放比例。
ts
function screenToDocument(
point: Point,
window: WindowGeometry,
viewport: CanvasViewport
): Point {
const x = (point.x - window.left - viewport.left) / window.scale
const y = (point.y - window.top - viewport.top) / window.scale
return inverseTransform({
x: x + viewport.scrollX,
y: y + viewport.scrollY
}, viewport.rotation)
}
转换后的坐标还要命中业务区域。画布可以插入新图层,图层面板可以解释为追加资源,只读预览区则应明确拒绝。同一个物理点在不同区域对应不同命令。
五、写入必须幂等且可撤销
跨设备链路可能发生"不确定成功":图片已经插入,但结果回传前连接中断,发送端随后重试。接收端必须按 transferId 返回第一次结果,不能重复创建图层。
ts
interface InsertResult {
code: 'OK' | 'DUPLICATE' | 'TARGET_INVALID' | 'UNSUPPORTED'
operationId?: string
layerId?: string
undoToken?: string
retryable: boolean
}
async function insertSharedImage(cmd: InsertCommand): Promise<InsertResult> {
const cached = await transferStore.find(cmd.transferId)
if (cached) return { ...cached, code: 'DUPLICATE' }
const target = resolveTarget(cmd.targetWindowId)
if (!target.ok) return { code: 'TARGET_INVALID', retryable: false }
const operation = await target.document.insertImage(cmd)
const result = {
code: 'OK', operationId: operation.id,
layerId: operation.layerId,
undoToken: operation.undoToken,
retryable: false
} as InsertResult
await transferStore.save(cmd.transferId, result)
return result
}
生产实现要处理"文档已写入、去重记录尚未保存"的事务缝隙。最好让 transferId 成为文档操作的业务唯一键,使进程恢复后仍能判断是否执行过。
六、确认策略由副作用决定
插入新图层并提供撤销,通常可以轻预览后执行;覆盖选区、替换原文件、上传云端或外发内容,则必须展示影响范围并确认。入口变短不代表权限扩大,碰一碰也不能绕过登录、隐私和只读限制。
七、互动卡片不是缩小版 App
普通服务卡片适合展示进度、完成签到、切换播放或打开结果。可以采用"三秒原则":用户能否在三秒内理解当前状态和下一步动作?如果卡片包含长表单、多级导航和复杂编辑,就应该跳回 App。
卡片不维护独立业务状态,而是读取统一快照:
ts
interface ProcessingSnapshot {
jobId: string
state: 'QUEUED' | 'RUNNING' | 'DONE' | 'FAILED'
progress: number
version: number
updatedAt: number
previewUri?: string
}
function reduceCard(s: ProcessingSnapshot): CardViewModel {
if (s.state === 'DONE') {
return { title: '处理完成', action: '查看结果' }
}
if (s.state === 'FAILED') {
return { title: '处理失败', action: '打开应用' }
}
return { title: '正在处理', progress: s.progress }
}
快照带版本号,旧状态不得覆盖新状态。卡片刷新失败时显示更新时间,不用过期进度误导用户。
八、快游戏互动卡片需要单独理解
官方快游戏互动卡片使用独立 RPK 包,具有专门的包命名、4×4 栅格、破框范围、Deeplink 和 qg.addInteractiveCard 等规则。这些规则不能直接套到普通 HarmonyOS 应用服务卡片上。
js
qg.addInteractiveCard({
cardId: 'daily-challenge',
success: () => console.info('card added'),
fail: (data, code) => console.error(code, data)
})
互动卡片应围绕领取奖励、查看挑战进度等单一目标;支付、广告和账号数据承接必须按当前快游戏规范逐条核对。
九、状态机阻止迟到结果
一次精准分享可以建模为 RECEIVED → VALIDATING → PREVIEW_READY → COMMITTING → COMPLETED,并允许进入 REJECTED、CANCELLED、TARGET_LOST、FAILED。终态不可再次迁移;用户取消或窗口关闭后,迟到的图片准备回调不能继续写入。
十、测试矩阵
- 目标窗口切换、关闭或文档版本变化;
- 画布缩放、旋转、滚动和多显示器缩放;
- 重复碰触、结果回传丢失和进程恢复;
- 图片过大、格式不支持、权限拒绝和磁盘不足;
- 卡片旧快照、离线、锁屏和未登录;
- 不支持新能力的设备回退到系统分享或 App 页面。

结语
精准分享的核心不是"传得更快",而是目标窗口和业务位置足够准确;互动卡片的核心不是"做得更多",而是状态可靠、动作最小。使用稳定身份、完整坐标转换、幂等事务、撤销令牌和统一领域快照,才能让短入口真正提高效率。