AR 眼镜之-HID 蓝牙戒指-接入方案
前言在 AR 眼镜的交互方案中手势控制是核心痛点之一传统的触控板、语音控制、按键等方式各有局限性而HID 蓝牙戒指作为一种新型输入设备能够提供更自然、更精准的操作体验。本文将详细介绍三种渐进式接入方案从简单到复杂逐步实现戒指控制 AR 眼镜的目标。核心流程戒指支持的手势事件前滑向前滑动后滑向后滑动进入点击/确认退出返回/取消1. 技术方案概述1.1 方案对比方案适用场景息屏支持优点缺点方案一前台界面监听App 前台运行时-实现简单无需系统权限App 后台或锁屏时失效方案二无障碍服务前台/后台/锁屏-无需修改戒指协议系统级拦截息屏时可能失效需用户授权方案三私有蓝牙协议全场景Y功能完整体验最优需要戒指厂商提供协议文档1.2 架构设计2. ⚛️ 方案一前台界面监听2.1 方案原理当蓝牙戒指作为 HID 设备连接到手机后戒指的手势操作会转化为标准的指针事件PointerEvent或按键事件KeyEvent通过在 Flutter 界面中使用 Listener 组件可以直接捕获这些事件。2.2 实现方案创建一个专门的中转 Widget负责监听戒指发出的指针事件滑动、点击、长按识别手势类型单击、双击、长按、滑动将手势转换为控制指令通过蓝牙/Wi-Fi 转发给 AR 眼镜2.2.1 核心代码Focus.ListenerWidget build(BuildContext context) { return Focus( focusNode: _focusNode, autofocus: true, onFocusChange: (hasFocus) { logI(焦点状态改变: $hasFocus, tag: RingToGlassesControllerWidget); }, child: Listener( // 2. 指针按下时开启手势判定管道 onPointerDown: _handlePointerDown, // 3. 指针移动时检查是否移动过大需取消点击 onPointerMove: _handlePointerMove, // 4. 指针抬起识别单击或双击 onPointerUp: _handlePointerUp, // 5. 娱乐模式下的纯移动事件用于滑动 onPointerHover: (PointerHoverEvent event) { if (event.delta.dy 1.0) { _internalEventStream.add(HOVER_DOWN); } else if (event.delta.dy -1.0) { _internalEventStream.add(HOVER_UP); } }, ), ); }2.2.2 完整代码RingToGlassesControllerWidget.dartimport dart:async; import package:flutter/gestures.dart; import package:flutter/material.dart; import package:flutter/services.dart; import package:mb_app/device/manager/device_setting_manager.dart; import package:mb_app/device/view/widgets/glasses_remote_controller_popup.dart; class RingToGlassesControllerWidget extends StatefulWidget { const RingToGlassesControllerWidget({Key? key}) : super(key: key); override StateRingToGlassesControllerWidget createState() _RingToGlassesControllerWidgetState(); } class _RingToGlassesControllerWidgetState extends StateRingToGlassesControllerWidget { final FocusNode _focusNode FocusNode(); final ListString _consoleLogs []; bool _isConnectedToGlasses false; bool _isConnecting false; final StreamControllerString _internalEventStream StreamControllerString.broadcast(); StreamSubscriptionString? _eventForwardingSub; Offset? _pointerDownPosition; DateTime? _pointerDownTime; Timer? _longPressTimer; Timer? _clickDelayTimer; DateTime? _lastClickTime; static const Duration _longPressThreshold Duration(milliseconds: 500); static const Duration _clickThreshold Duration(milliseconds: 250); static const Duration _doubleClickThreshold Duration(milliseconds: 300); static const double _moveTolerance 15.0; override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) _focusNode.requestFocus(); }); _eventForwardingSub _internalEventStream.stream.listen((command) { _sendDataToGlasses(command); }); _connectToGlasses(); } override void dispose() { _focusNode.dispose(); _eventForwardingSub?.cancel(); _internalEventStream.close(); _longPressTimer?.cancel(); _clickDelayTimer?.cancel(); _disconnectFromGlasses(); super.dispose(); } void _connectToGlasses() async { if (_isConnectedToGlasses || _isConnecting) return; setState(() { _isConnecting true; _logToConsole(正在尝试建立与眼镜的无线通信链路...); }); await Future.delayed(const Duration(seconds: 2)); if (!mounted) return; setState(() { _isConnecting false; _isConnectedToGlasses true; _logToConsole( 眼镜连接成功可以开始中转指环指令。); }); } void _sendDataToGlasses(String command) { if (!_isConnectedToGlasses) return; int code RemoteControlKeyCode.touchBarForward; if (command HOVER_DOWN) { code RemoteControlKeyCode.touchBarForward; } else if (command HOVER_UP) { code RemoteControlKeyCode.touchBarBackward; } else if (command CONFIRM_CLICK || command TOUCH_CLICK) { code RemoteControlKeyCode.touchBarSingleClick; } else if (command TOUCH_DOUBLE_CLICK) { code RemoteControlKeyCode.touchBarDoubleClick; } else if (command TOUCH_LONG_PRESS) { code RemoteControlKeyCode.touchBarLongPressed; } unawaited(deviceSettingManager.sendCmdControlVirtualKey(code)); } void _handlePointerDown(PointerDownEvent event) { _pointerDownPosition event.localPosition; _pointerDownTime DateTime.now(); _longPressTimer?.cancel(); _longPressTimer Timer(_longPressThreshold, () { if (_pointerDownPosition ! null) { _internalEventStream.add(TOUCH_LONG_PRESS); _cleanupGestureState(); } }); } void _handlePointerMove(PointerMoveEvent event) { if (_pointerDownPosition null) return; final double distance (event.localPosition - _pointerDownPosition!).distance; if (distance _moveTolerance) { _longPressTimer?.cancel(); _pointerDownPosition null; } } void _handlePointerUp(PointerUpEvent event) { _longPressTimer?.cancel(); if (_pointerDownPosition null || _pointerDownTime null) return; final DateTime now DateTime.now(); final Duration pressDuration now.difference(_pointerDownTime!); if (pressDuration _clickThreshold) { if (_lastClickTime ! null now.difference(_lastClickTime!) _doubleClickThreshold) { _clickDelayTimer?.cancel(); _internalEventStream.add(TOUCH_DOUBLE_CLICK); _lastClickTime null; } else { _lastClickTime now; _clickDelayTimer?.cancel(); _clickDelayTimer Timer(_doubleClickThreshold, () { _internalEventStream.add(TOUCH_CLICK); _lastClickTime null; }); } } _cleanupGestureState(); } void _cleanupGestureState() { _pointerDownPosition null; _pointerDownTime null; _longPressTimer?.cancel(); } override Widget build(BuildContext context) { return Focus( focusNode: _focusNode, autofocus: true, child: Listener( onPointerDown: _handlePointerDown, onPointerMove: _handlePointerMove, onPointerUp: _handlePointerUp, onPointerHover: (PointerHoverEvent event) { if (event.delta.dy 1.0) { _internalEventStream.add(HOVER_DOWN); } else if (event.delta.dy -1.0) { _internalEventStream.add(HOVER_UP); } }, onPointerSignal: (PointerSignalEvent pointerSignal) { if (pointerSignal is PointerScrollEvent) { if (pointerSignal.scrollDelta.dy 0) { _internalEventStream.add(SLIDE_DOWN); } else if (pointerSignal.scrollDelta.dy 0) { _internalEventStream.add(SLIDE_UP); } } }, child: Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text(OnCtrl 中转中心, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), _buildStatusBadge(), ], ), const SizedBox(height: 12), Text( _isConnectedToGlasses ? 请操作指环指令将通过手机自动实时透传至眼镜... : 等待眼镜连接通道准备就绪..., style: TextStyle(color: Colors.grey.shade600, fontSize: 13), ), const SizedBox(height: 12), const Text(实时同步日志:, style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500)), const SizedBox(height: 6), Expanded( child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(6), ), child: _consoleLogs.isEmpty ? const Center(child: Text(等待指环物理信号..., style: TextStyle(color: Colors.greenAccent, fontSize: 12))) : ListView.builder( itemCount: _consoleLogs.length, itemBuilder: (context, index) Text( _consoleLogs[index], style: const TextStyle(color: Colors.greenAccent, fontFamily: monospace, fontSize: 11), ), ), ), ), ], ), ), ), ); } Widget _buildStatusBadge() { if (_isConnecting) { return const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.orange)); } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _isConnectedToGlasses ? Colors.green.shade100 : Colors.red.shade100, borderRadius: BorderRadius.circular(4), ), child: Text( _isConnectedToGlasses ? 眼镜在线 : 眼镜离线, style: TextStyle( color: _isConnectedToGlasses ? Colors.green.shade800 : Colors.red.shade800, fontSize: 11, fontWeight: FontWeight.bold, ), ), ); } }2.3 方案总结适用场景开发调试阶段、简单演示、用户主动打开控制界面时核心问题当 App 进入后台或手机锁屏时Listener 将无法接收事件导致戒指控制失效。3. 方案二无障碍服务3.1 方案原理Android 的 AccessibilityService 提供了系统级的事件监听能力当戒指作为 HID 设备发送按键事件时无障碍服务可以在任何应用包括系统界面中拦截这些事件无论 App 是否在前台运行。关键特性支持前台、后台、锁屏状态下的事件监听限制息屏状态下大多数 Android 设备会暂停无障碍服务3.2 实现方案3.2.1 无障碍服务配置accessibility_service_config.xml?xml version1.0 encodingutf-8? accessibility-service xmlns:androidhttp://schemas.android.com/apk/res/android android:accessibilityEventTypestypeAllMask android:accessibilityFeedbackTypefeedbackGeneric android:accessibilityFlagsflagDefault|flagRequestFilterKeyEvents android:canRequestFilterKeyEventstrue android:descriptionstring/accessibility_service_description android:notificationTimeout100 /3.2.2 服务描述strings.xmlstring nameaccessibility_service_description用于在后台实时转发智能指环的多媒体控制指令至眼镜终端。/string3.2.3 核心代码RingAccessibilityService.ktpackage com.metabounds.mojieglass.plugin.ring import android.accessibilityservice.AccessibilityService import android.util.Log import android.view.KeyEvent import android.view.accessibility.AccessibilityEvent import com.metabounds.libglass.v2.req.CmdV2ReqControllerKey import com.metabounds.mojieglass.bluetooth.manager.MBBTManager class RingAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent?) { // 娱乐模式下指环动作可能触发窗口状态改变或焦点变化 // 如果指环发送特定 Accessibility 类型的节点点击可以在此拦截 } override fun onInterrupt() { Log.e(TAG, 无障碍服务被强行中断) } override fun onServiceConnected() { super.onServiceConnected() Log.i(TAG, 指环后台无障碍中转服务已成功连接) } override fun onKeyEvent(event: KeyEvent): Boolean { val keyCode event.keyCode val action event.action if (action KeyEvent.ACTION_DOWN) { when (keyCode) { KeyEvent.KEYCODE_MEDIA_NEXT - BTManager.sendCmd(CmdV2ReqControllerKey(0)) KeyEvent.KEYCODE_MEDIA_PREVIOUS - BTManager.sendCmd(CmdV2ReqControllerKey(1)) KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY - BTManager.sendCmd(CmdV2ReqControllerKey(2)) KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_ESCAPE - BTManager.sendCmd(CmdV2ReqControllerKey(11)) } Log.i(TAG, onKeyEvent: keyCode$keyCode) return true } return super.onKeyEvent(event) } companion object { private const val TAG RingAccessibility } }3.2.4 注册服务AndroidManifest.xmlservice android:name.plugin.ring.RingAccessibilityService android:exportedtrue android:label指环无障碍控制服务 android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE intent-filter action android:nameandroid.accessibilityservice.AccessibilityService / /intent-filter meta-data android:nameandroid.accessibilityservice android:resourcexml/accessibility_service_config / /service3.2.5 引导用户授权MainActivity.ktoverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... 其他初始化 openAccessibilitySettings() } private fun openAccessibilitySettings() { try { val intent Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } startActivity(intent) } catch (e: Exception) { e.printStackTrace() } }3.3 方案总结适用场景需要后台持续监听、锁屏状态下使用核心问题用户需要手动在系统设置中开启无障碍权限流程较复杂息屏状态下部分 Android 厂商会限制无障碍服务运行不同厂商的系统对无障碍服务的行为可能存在差异4. 方案三私有蓝牙协议4.1 方案原理目前市面上的 HID 蓝牙戒指大多基于标准 HID 协议工作将手势转换为键盘/鼠标事件但这种方式存在以下问题功能受限只能映射到有限的标准按键兼容性差不同设备对 HID 事件的处理不一致体验不佳无法区分戒指的特定手势如前滑、后滑最佳方案与戒指厂商合作开放私有蓝牙协议通过 BLE GATT 特征值直接传输手势数据。4.2 协议设计// 监听 GATT 特征值变化 ringGatt?.setCharacteristicNotification(gestureCharacteristic, true) val descriptor gestureCharacteristic.getDescriptor(UUID.fromString(00002902-0000-1000-8000-00805F9B34FB)) descriptor?.value BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE ringGatt?.writeDescriptor(descriptor) // 接收手势事件 override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { val data characteristic.value val eventType data[0].toInt() val direction data[1].toInt() val pressure data[2].toInt() when (eventType) { 0x01 - handlePress(direction) 0x02 - handleRelease(direction) 0x03 - handleSlide(direction) 0x04 - handleLongPress(direction) } } private fun handleSlide(direction: Int) { val command when (direction) { 0x01 - CmdV2ReqControllerKey(0) // 上滑 0x02 - CmdV2ReqControllerKey(1) // 下滑 0x03 - CmdV2ReqControllerKey(2) // 左滑 0x04 - CmdV2ReqControllerKey(3) // 右滑 else - return } BTManager.sendCmd(command) }4.3 方案总结适用场景正式产品、追求最佳体验优点功能完整支持自定义手势不受标准 HID 限制体验最优低延迟、高精度支持压力感应息屏支持BLE 连接在息屏状态下仍可保持扩展性强可通过协议升级增加新功能缺点需要戒指厂商配合开放协议开发成本较高需要双方联调5. 方案对比与选择建议5.1 综合对比维度方案一方案二方案三开发周期1-2 天3-5 天2-4 周用户体验⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐后台运行❌✅✅息屏支持❌❌✅手势种类有限有限丰富系统兼容性高中高用户授权无需需要需要5.2 选择建议初期使用方案一快速验证戒指与眼镜的联动效果中期接入方案二在 App 后台和锁屏时也能工作长期推动戒指厂商开放私有协议实现方案三6. ⚡ 常见问题与解决方案6.1 戒指连接不稳定问题蓝牙连接频繁断开尤其在手机息屏后。解决方案使用 BLE 长连接设置合适的连接参数在 Android 端申请FOREGROUND_SERVICE_CONNECTED_DEVICE权限添加电池优化白名单防止系统杀死蓝牙进程uses-permission android:nameandroid.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE / uses-permission android:nameandroid.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS /6.2 事件识别不准确问题滑动和点击事件混淆长按判定不准确。解决方案调整手势识别参数时间阈值、位移阈值使用状态机管理手势状态在硬件层面戒指端增加防抖处理6.3 无障碍服务被系统关闭问题部分国产 ROM 会在后台自动关闭无障碍服务。解决方案在 App 启动时检查服务状态未开启则引导用户使用前台服务保活增加服务优先级提供详细的开启指引文档7. 小结本文介绍了三种 HID 蓝牙戒指接入 AR 眼镜的方案从简单到复杂逐步递进方案一前台界面监听实现简单适合开发调试和基础演示方案二无障碍服务系统级拦截支持后台和锁屏状态但息屏受限方案三私有蓝牙协议功能完整体验最优是正式产品的最佳选择在实际项目中建议采用渐进式策略先用方案一验证可行性再用方案二提升用户体验最终推动戒指厂商开放私有协议实现方案三。

相关新闻

免费足球数据API实战:获取英超西甲实时数据

免费足球数据API实战:获取英超西甲实时数据

1. 足球数据API实战:免费获取英超、西甲实时数据全攻略 作为一名长期跟踪足球数据的开发者,我深知获取可靠比赛数据的痛点。最近在开发一款足球分析工具时,发现football-data.org提供的免费API完全能满足实时比分、积分榜甚至高级xG数据的获…

2026/7/22 3:06:15阅读更多 →
C#中ArrayList类相关方法

C#中ArrayList类相关方法

ArrayList类本质上是一个Object数组,在使用时需要引用System.Collections命名空间,其提供多样化的增删改查,遍历与装箱拆箱的方法。using System; using System.Collections; namespace lesson01 {class Program {static void Main(string[]…

2026/7/22 3:04:15阅读更多 →
YUM包管理工具详解:从入门到实战

YUM包管理工具详解:从入门到实战

1. 认识YUM:Linux世界的软件管家 第一次接触Linux系统时,最让我困惑的就是软件安装方式。与Windows下双击exe的直观操作不同,Linux世界里的软件管理自成体系。而YUM(Yellowdog Updater Modified)正是RHEL/CentOS系Linu…

2026/7/22 3:04:14阅读更多 →
快递包装新国标解读:绿色物流的减量化实践

快递包装新国标解读:绿色物流的减量化实践

1. 快递包装新国标实施背景与行业痛点快递包装过度问题已经成为当前物流行业最突出的环保难题之一。根据行业调研数据显示,2022年我国快递业务量达到1105.8亿件,产生的包装废弃物超过2000万吨,其中约30%属于过度包装。这些过度包装不仅造成资…

2026/7/22 7:39:16阅读更多 →
随笔:宜搭报表部门筛选问题

随笔:宜搭报表部门筛选问题

宜搭报表筛选器选择数据集中的部门组件,设置多选,发现选单个部门可以筛选,但是一但多选就筛选不出来需要自己用公式加工一下

2026/7/22 7:39:16阅读更多 →
【Bug已解决】CI fails with transformers v5.0.0: AttributeError: ‘GptOssConfig‘ object has no attribute ‘n

【Bug已解决】CI fails with transformers v5.0.0: AttributeError: ‘GptOssConfig‘ object has no attribute ‘n

【Bug已解决】CI fails with transformers v5.0.0: AttributeError: GptOssConfig object has no attribute num_experts 解决方案原始报错:CI fails with transformers v5.0.0: AttributeError: GptOssConfig object has no attribute num_experts 场景&#xff1a…

2026/7/22 7:39:16阅读更多 →
深入解析HPI接口FIFO刷新与中断处理机制

深入解析HPI接口FIFO刷新与中断处理机制

1. HPI接口:嵌入式高速通信的“咽喉要道”在嵌入式系统,尤其是数字信号处理器(DSP)为核心的系统中,外部主机(如ARM、FPGA或PC)与DSP内核之间的数据交换效率,往往是整个系统性能的瓶颈…

2026/7/22 7:39:16阅读更多 →
书籍推荐 | VirtualLab Fusion 物理光学实验教程

书籍推荐 | VirtualLab Fusion 物理光学实验教程

前言在物理光学中,认为光是一种电磁波。在光的电磁场理论基础上,研究光在介质中的传播规律,如光的干涉、光的衍射、光的偏振等物理现象,进而研究这些规律和现象的应用。它是一门经典理论与近代技术相结合的应用性很强的课程。由于…

2026/7/22 7:39:16阅读更多 →
快手大模型算法岗面试要点与实战解析

快手大模型算法岗面试要点与实战解析

1. 快手大模型算法岗面试深度解析 去年面试季,我作为面试官参与了快手大模型算法岗的招聘工作。这个岗位的面试问题之细致、考察范围之广,让不少候选人都直呼"压力山大"。今天我就从面试官视角,还原这场"魔鬼面试"的真实…

2026/7/22 7:37:16阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业做小程序,最常见的矛盾是预算有限,但又不希望功能太单薄;没有技术团队,但又希望后续能自己运营;想快速上线,又担心隐性收费和售后失联。选型时如果只看“低价套餐”或“案例数量”,很容…

2026/7/22 0:01:17阅读更多 →
GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

企业做营销,最怕钱花完了,资产没有留下。 效果广告能带来一段时间的曝光,但预算停止后,流量往往也随之停止。短视频内容可能在几天内冲高,也可能很快沉下去。AI搜索时代,企业需要重新思考一个问题&#xff…

2026/7/22 0:01:17阅读更多 →
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复 一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了 Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接…

2026/7/22 0:01:17阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/21 18:53:30阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/21 18:53:30阅读更多 →