Android Studio(Java)中ARCore如何实现3D对象点击事件处理
ARCore Android(Java)3D对象/增强图像点击事件实现
AR场景下无法响应3D对象、增强图像点击的核心原因是:3D内容是渲染在GL Surface上的,不存在原生Android View的点击事件体系,直接给渲染对象、Fragment设置普通点击监听不会生效,必须通过射线碰撞检测+渲染对象映射的方式实现。
核心实现流程
- 拦截AR场景视图的触摸事件,获取触摸点的屏幕坐标
- 调用ARCore的
hitTest()接口做从屏幕点到AR空间的射线碰撞检测,过滤跟踪状态无效的命中结果 - 维护锚点/增强图像和对应渲染3D对象的映射表,命中后匹配到对应对象,触发业务层点击逻辑
可直接复用的Java代码
1. 全局变量初始化
// 锚点和3D渲染对象的映射表,用于手动放置的永久锚点场景 private final HashMap<Anchor, Object> anchorModelMap = new HashMap<>(); // 增强图像和3D渲染对象的映射表,用于AugmentedImage场景 private final HashMap<Integer, Object> augImageModelMap = new HashMap<>(); private ArFragment arFragment;
2. 触摸事件监听注册
在onCreate中完成ArFragment初始化后注册触摸监听,不要在视图未加载完成时注册:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ar); arFragment = (ArFragment) getSupportFragmentManager().findFragmentById(R.id.ar_fragment); arFragment.getArSceneView().setOnTouchListener((view, motionEvent) -> { // 仅响应手指抬起事件,避免滑动误触发 if (motionEvent.getAction() != MotionEvent.ACTION_UP) return false; Frame currentFrame = arFragment.getArSceneView().getArFrame(); if (currentFrame == null) return false; // 执行碰撞检测 List<HitResult> hitResultList = currentFrame.hitTest(motionEvent.getX(), motionEvent.getY()); for (HitResult hit : hitResultList) { Trackable hitTrackable = hit.getTrackable(); // 匹配手动放置的永久锚点对象 if (hitTrackable instanceof Anchor) { Anchor hitAnchor = (Anchor) hitTrackable; if (hitAnchor.getTrackingState() != TrackingState.TRACKING) continue; Object targetModel = anchorModelMap.get(hitAnchor); if (targetModel != null) { // 这里写3D对象点击后的业务逻辑,比如弹信息框、播放动画 onModelClick(targetModel); return true; } } // 匹配增强图像对象 if (hitTrackable instanceof AugmentedImage) { AugmentedImage hitImage = (AugmentedImage) hitTrackable; if (hitImage.getTrackingState() != TrackingState.TRACKING) continue; Object targetModel = augImageModelMap.get(hitImage.getIndex()); if (targetModel != null) { // 这里写增强图像点击后的业务逻辑 onAugImageClick(hitImage, targetModel); return true; } } } // 未命中任何可交互对象,事件向下传递 return false; }); }
3. 放置锚点时绑定映射关系
放置永久锚点、加载3D模型时,必须把锚点和对应的模型对象存入映射表,否则碰撞检测后无法匹配到对应业务对象:
// 平面点击放置模型的示例逻辑 private void placePermanentAnchor(HitResult hitResult, ModelRenderable model) { Anchor newAnchor = hitResult.createAnchor(); AnchorNode anchorNode = new AnchorNode(newAnchor); anchorNode.setRenderable(model); arFragment.getArSceneView().getScene().addChild(anchorNode); // 关键:存入映射表,value可以替换成你自己封装的业务模型类 anchorModelMap.put(newAnchor, model); }
4. 增强图像检测时绑定映射关系
在增强图像更新回调中,检测到可跟踪的图像并挂载3D内容时,同样要存入映射表:
@Override public void onUpdate(FrameTime frameTime) { Frame frame = arFragment.getArSceneView().getArFrame(); if (frame == null) return; Collection<AugmentedImage> updatedImages = frame.getUpdatedTrackables(AugmentedImage.class); for (AugmentedImage image : updatedImages) { if (image.getTrackingState() == TrackingState.TRACKING && !augImageModelMap.containsKey(image.getIndex())) { // 挂载3D模型的常规逻辑 Anchor imgAnchor = image.createAnchor(image.getCenterPose()); AnchorNode imgNode = new AnchorNode(imgAnchor); imgNode.setRenderable(augImageModel); arFragment.getArSceneView().getScene().addChild(imgNode); // 关键:存入映射表 augImageModelMap.put(image.getIndex(), augImageModel); } } }
常见避坑点
- 不要直接依赖Sceneform节点自带的
setOnClickListener,低版本SDK存在事件穿透、跟踪丢失后仍响应点击的bug,手动实现hitTest匹配稳定性更高 - 所有命中判断必须加
TrackingState.TRACKING校验,否则跟踪丢失的锚点、未识别成功的图像也会被判定为命中,出现点空白区域触发点击的异常 - 锚点移除、增强图像跟踪丢失超过3秒后,要及时从映射表中移除对应条目,避免内存泄漏
- 触摸事件不要拦截
ACTION_DOWN和ACTION_MOVE,否则会导致场景旋转、平移手势失效
内容的提问来源于stack exchange,提问作者Paterne K.
相关产品推荐
相关产品推荐

