
作者手记:这不是一篇标准的 API 教程。而是一次真实开发历程的复盘——关于如何用一行 JSON,在 Rokid Glasses 的透明镜片上,“画”出一个能救命的远程协作界面。过程中踩过的坑、绕过的弯、灵光一现的解法,我都写在这里。如果你也想让代码在 AR 世界里“显形”,请继续读下去。
上个月,我接到一个来自工业客户的紧急需求:
“我们的工程师在野外检修高压变电站,遇到一个从未见过的故障。打电话描述不清,视频又太卡。能不能让总部专家‘看到’现场,并直接在他眼前‘画’出操作指引?”
听起来像科幻片?但 Rokid Glasses + CXR-M SDK,真能实现。
传统方案无非两种:
我意识到:关键不是“看到”,而是“精准叠加”。 而 CXR-M SDK 的 自定义页面(Custom View) 功能,恰好提供了“无需写眼镜端代码,即可动态渲染 UI”的能力——这正是突破口。
很多人以为,调用 `connectBluetooth()` 成功返回 `onConnected()`,就算连上了。 **错。**
我在测试时发现:蓝牙连上了,但 openCustomView() 始终失败,回调 onOpenFailed(-1)。
翻遍文档,才注意到一句不起眼的提示:
“自定义页面依赖蓝牙通道稳定传输,若连接后未完成设备信息同步,将无法初始化 UI。”
原来,onConnected() 只代表底层链路通了,但 设备元数据(如 glassesType)尚未就绪。
正确做法:必须等待 onConnectionInfo() 回调,拿到 socketUuid 和 macAddress 后,才算“真正可用”。
// 错误示范:连上就开 UI
override fun onConnected() {
openCustomView(json) // ❌ 可能失败
}
// 正确做法:等信息同步完成
override fun onConnectionInfo(uuid, mac, _, _) {
if (uuid != null && mac != null) {
// 此时才安全
openCustomView(json) // ✅
}
}教训:SDK 的“连接成功”是分阶段的。别急,让数据先跑完一圈。
最颠覆我认知的,是 **UI 不用写 XML,而是写 JSON**。
起初我很抗拒:“这不就是把前端那一套搬过来?性能能行吗?” 但当我真正用起来,才发现这是 面向 AR 场景的精妙设计。
+ **动态性**:远程专家在手机上画个圈,App 立刻生成一段 JSON,推给眼镜——无需预装任何资源。 + **轻量级**:只传变更,不传整个页面。比如只更新 `text` 字段,其他不动。 + **解耦**:眼镜端只负责渲染,逻辑全在手机端,符合 CXR-M “手机为主” 的定位。
文档里写:“图片需使用绿色通道(#00FF00)”。 我一开始没当回事,直接传了个彩色 PNG,结果眼镜上一片漆黑。
后来才明白:Rokid Glasses 的光学显示模组 只对特定波长敏感,SDK 为简化开发者负担,强制将绿色通道映射为“可见像素”,其他通道丢弃。
解决方案:上传前做颜色过滤。
// 仅保留绿色通道
val paint = Paint().apply {
colorFilter = LightingColorFilter(0x00FF00, 0x000000)
}
canvas.drawBitmap(original, 0f, 0f, paint)💡 经验:所有图标(箭头、圆圈、警告标志)都做成纯绿色 SVG,再转 Base64,清晰又省流量。
我最初用 `LinearLayout` 做界面:顶部指令,中部内容,底部状态。 结果在真实场景中崩溃了——**用户低头看设备时,UI 跑到视野外去了**。
AR UI 的核心原则是:锚定物理空间,而非屏幕坐标。 但 CXR-M 并不提供空间锚点(那是更高阶 SDK 的事),我们只能“模拟”。
灵光一现:用 RelativeLayout + layout_centerInParent,让关键元素始终居中视野。
{
"type": "RelativeLayout",
"props": { "layout_width": "match_parent", "layout_height": "match_parent" },
"children": [
{
"type": "ImageView",
"props": {
"id": "annotation",
"layout_width": "80dp",
"layout_height": "80dp",
"name": "arrow_down",
"layout_centerInParent": "true" // 👈 关键!
}
}
]
}这样,无论用户怎么转头,那个“向下箭头”始终指向视野中央——专家说“看这里”,用户一眼就看到。
反思:在 AR 里,UI 不是“界面”,而是“指示器”。布局要服务于空间感知。
远程协作不是单向广播。专家发指令,现场人员得能“确认”或“求助”。
CXR-M 提供了 AiEventListener,监听功能键长按事件:
override fun onAiKeyDown() {
// 用户长按确认
sendSignalToExpert("confirmed")
updateInstruction("✅ 已执行")
}但问题来了:用户可能误触,或想取消。
于是我们设计了双状态反馈:
sendTtsContent())而这一切,只需在手机端监听同一个事件,通过 按压时长区分意图——眼镜端无需任何改动。
这就是 CXR-M 的哲学:复杂逻辑留在手机,眼镜只做“显示+简单输入”。
在一次压力测试中,我们连续推送 20 次标注更新,眼镜直接卡死。
排查发现:每次 updateCustomView() 都是一次完整 JSON 解析+渲染。高频调用会阻塞主线程。
优化策略:
Handler 延迟 100ms,把多次更新合并为一次。val updateHandler = Handler(Looper.getMainLooper())
var pendingUpdates = mutableListOf<UpdateItem>()
fun scheduleUpdate(item: UpdateItem) {
pendingUpdates.add(item)
updateHandler.removeCallbacks(updateRunnable)
updateHandler.postDelayed(updateRunnable, 100)
}deinitWifiP2P()。省电,也避免干扰蓝牙。我们将系统部署到某电网公司的巡检团队。
反馈惊人:
但也有意外发现:
“专家画的箭头太小,阳光下看不清。”
于是我们紧急迭代:
LinearLayout 做底);这再次证明:AR 应用必须在真实光照、噪声、运动场景下测试。实验室的“完美 UI”,可能在现场一文不值。
这次项目让我意识到,Custom View 的潜力远不止远程协作。
+ 系统自动高亮目标货架(绿色边框); + 拣货完成,自动打钩(✅ 图标); + 错拣时,显示红色警告(⚠️ 图标)。
+ 医生视野中叠加血管位置(绿色线条); + 关键步骤前,弹出操作确认(“是否切开?”); + 语音记录手术日志。
+ 在现实桌面“画”出棋盘; + 玩家用手势(模拟)移动棋子; + 系统实时更新状态。
核心逻辑不变:手机计算,眼镜显示。CXR-M 让这一切变得轻量、快速、低成本。
如果你也想基于 CXR-M SDK 开发,这里有几点血泪经验:
BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT,缺一不可。setCustomViewListener 监听 onOpened/onClosed,别靠 Thread.sleep() 猜。开发这个系统的过程中,我常想起一句话:
“技术的终极目标,是让工具消失,只留下人与人的连接。”
Rokid Glasses 不是炫技的玩具,而是 知识传递的管道。 CXR-M SDK 的 Custom View,正是打通这条管道的关键阀门。
它让我们用最熟悉的手机开发范式,去构建最前沿的 AR 体验。 无需 OpenGL,无需 Unity,只需一段 JSON,就能在真实世界“画”出指引、标注、答案。
这,就是我理解的 AI+AR 生态——不是取代人,而是放大人的能力。
如果你也有一个想“画”在现实世界中的想法,
不妨从 openCustomView() 开始。
也许下一次,拯救现场的,就是你的代码。
后记:本文所有方案均已通过 Rokid CXR-M SDK v1.0.1-Preview 验证。完整代码与图标资源包,欢迎在 Rokid 开发者社区交流。期待你的创意,让 AR 真正落地生根。