You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android ARCore:修改平移与旋转的默认手势

嘿,我之前刚好折腾过Sceneform的手势自定义,这个需求完全可以通过重写手势检测逻辑来实现,给你一步步拆解怎么做:

自定义Sceneform手势交互实现方案

首先得明确:Sceneform默认的单指平移、双指旋转是由TransformableNode自带的TransformationSystem处理的,我们要做的就是替换这套默认逻辑,实现自己的触摸事件响应。

1. 写一个自定义手势检测器

先创建一个CustomGestureDetector类,用来区分单指和双指操作,同时定义回调接口让我们后续处理具体的模型变换:

public class CustomGestureDetector extends BaseGestureDetector {
    private final ScaleGestureDetector scaleDetector;
    private final GestureDetector tapDetector;
    private MotionEvent lastEvent;
    private OnCustomGestureListener listener;

    // 自定义手势回调,把不同操作的事件抛出去
    public interface OnCustomGestureListener {
        void onSingleFingerRotate(float deltaX); // 单指左右滑动旋转
        void onTwoFingerPan(float dx, float dy); // 双指平移
    }

    public CustomGestureDetector(Context context, OnCustomGestureListener listener) {
        this.listener = listener;
        scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        tapDetector = new GestureDetector(context, new TapListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        scaleDetector.onTouchEvent(event);
        tapDetector.onTouchEvent(event);

        // 先记录上一次的触摸事件,用来计算偏移量
        if (lastEvent == null) {
            lastEvent = MotionEvent.obtain(event);
            return true;
        }

        int pointerCount = event.getPointerCount();
        switch (pointerCount) {
            case 1:
                // 单指操作:左右滑动触发旋转
                if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    float deltaX = event.getX() - lastEvent.getX();
                    listener.onSingleFingerRotate(deltaX);
                }
                break;
            case 2:
                // 双指操作:计算双指中点的偏移,用来平移模型
                float currMidX = (event.getX(0) + event.getX(1)) / 2;
                float currMidY = (event.getY(0) + event.getY(1)) / 2;
                float lastMidX = (lastEvent.getX(0) + lastEvent.getX(1)) / 2;
                float lastMidY = (lastEvent.getY(0) + lastEvent.getY(1)) / 2;
                float dx = currMidX - lastMidX;
                float dy = currMidY - lastMidY;
                listener.onTwoFingerPan(dx, dy);
                break;
        }

        lastEvent.recycle();
        lastEvent = MotionEvent.obtain(event);
        return true;
    }

    // 如果你需要保留缩放功能,可以在这里实现,不需要就留空
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {}

    // 处理点击类的手势,比如重置模型位置之类的,按需添加
    private class TapListener extends GestureDetector.SimpleOnGestureListener {}
}

2. 在AR场景中替换默认手势

找到你的ARFragment或者Activity,获取到要操作的TransformableNode,然后禁用默认手势,绑定我们自己的检测器:

// 假设你已经通过加载模型创建了TransformableNode
TransformableNode modelNode = ...;
ARSceneView sceneView = arFragment.getArSceneView();

// 第一步:禁用Sceneform默认的手势识别
modelNode.getTransformationSystem().clearGestureRecognizers();

// 第二步:初始化自定义手势检测器,实现具体的模型变换逻辑
CustomGestureDetector customDetector = new CustomGestureDetector(getContext(), new CustomGestureDetector.OnCustomGestureListener() {
    @Override
    public void onSingleFingerRotate(float deltaX) {
        // 调整旋转灵敏度:deltaX乘以的系数越大,旋转越快,按需调整
        float rotationDegree = deltaX * 0.4f;
        // 绕Y轴旋转(左右滑对应水平旋转)
        Quaternion rotation = Quaternion.axisAngle(new Vector3(0, 1, 0), rotationDegree);
        modelNode.setWorldRotation(modelNode.getWorldRotation().multiply(rotation));
    }

    @Override
    public void onTwoFingerPan(float dx, float dy) {
        // 将屏幕坐标的偏移转换为世界坐标的平移,Z值可以根据模型距离调整,影响平移速度
        Camera camera = sceneView.getScene().getCamera();
        Vector3 worldDelta = camera.screenToWorldPoint(new Vector3(dx, -dy, 0));
        // 更新模型位置
        modelNode.setWorldPosition(modelNode.getWorldPosition().add(worldDelta));
    }
});

// 第三步:将自定义检测器绑定到场景的触摸事件
sceneView.getScene().addOnTouchListener((v, event) -> customDetector.onTouchEvent(event));

3. 细节优化建议

  • 旋转灵敏度:可以根据实际体验调整rotationDegree = deltaX * 0.4f里的系数,数值越大旋转越灵敏
  • 平移速度:screenToWorldPoint的Z参数如果设得更大,平移速度会变慢,反之更快,你可以根据模型和相机的距离动态调整
  • 误触防护:可以给单指旋转加个阈值,比如当deltaX的绝对值大于5才触发旋转,避免轻微滑动就误操作
  • 保留缩放:如果需要保留双指缩放,只需要在ScaleListener里实现onScale方法,调整模型的缩放比例就行

我当时也是翻了好久官方文档没找到明确说明,后来自己啃了默认手势的源码才搞明白这套逻辑,上面的代码应该能直接满足你的需求啦!

内容的提问来源于stack exchange,提问作者vtodorova

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:01:37