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

Android M及以上版本实现显示触摸操作的技术方案咨询

解决Android M+无法通过Settings.System开启触摸显示的替代方案

我之前也踩过这个坑,Android 6.0(API 23)之后谷歌大幅收紧了系统安全设置的权限,Settings.System.putInt(context.getContentResolver(), "show_touches", 1)这类修改私有安全设置的操作,只有系统签名的应用或者持有特定系统权限的应用才能执行。哪怕把targetSdk降到22,在高版本系统上也会逐渐被限制,完全不符合你要使用高版本新特性的需求。下面给你几个可行的替代方案:

方案1:利用辅助功能(AccessibilityService)实现全局触摸显示

这是普通APP能实现全局触摸显示的唯一合法途径——辅助功能拥有监听系统级触摸事件的权限,能绕过普通APP的权限限制:

  1. 创建自定义AccessibilityService子类,重写onAccessibilityEvent方法监听触摸事件;
  2. 通过WindowManager添加一个全局悬浮视图,在触摸事件触发时,实时更新悬浮视图的位置并绘制触摸点(比如半透明圆圈);
  3. 在Manifest中注册这个Service,配置对应的辅助功能声明,引导用户在系统设置中开启你的服务。

核心代码片段示例:

public class TouchShowAccessibilityService extends AccessibilityService {
    private WindowManager windowManager;
    private View touchPointView;
    private WindowManager.LayoutParams params;

    @Override
    public void onCreate() {
        super.onCreate();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        touchPointView = new View(this);
        // 自定义触摸点样式,比如一个半透明的圆形drawable
        touchPointView.setBackgroundResource(R.drawable.touch_indicator); 
        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                PixelFormat.TRANSLUCENT
        );
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // 处理触摸按下、移动等事件,更新触摸点位置
        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED 
            || event.getEventType() == AccessibilityEvent.TYPE_VIEW_LONG_CLICKED) {
            int x = (int) event.getX();
            int y = (int) event.getY();
            params.x = x - touchPointView.getWidth()/2;
            params.y = y - touchPointView.getHeight()/2;
            windowManager.updateViewLayout(touchPointView, params);
        }
    }

    @Override
    public void onInterrupt() {}
}

注意:用户需要手动在系统设置的「辅助功能」里开启你的服务,你可以在APP内做引导跳转,提升用户体验。

方案2:仅在自身APP内显示触摸操作(无需系统权限)

如果你的需求只是在自己的APP内显示触摸轨迹,那完全不需要依赖系统权限,直接在APP层面处理即可:

  • 在BaseActivity中重写dispatchTouchEvent方法,监听所有触摸事件;
  • 创建一个自定义OverlayView,添加到Activity的DecorView中;
  • 在触摸事件触发时,在OverlayView上绘制触摸点或轨迹。

核心代码示例:

public class BaseActivity extends AppCompatActivity {
    private TouchOverlayView touchOverlayView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        touchOverlayView = new TouchOverlayView(this);
        getWindow().getDecorView().addView(touchOverlayView);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        touchOverlayView.updateTouchPosition(ev);
        return super.dispatchTouchEvent(ev);
    }

    private static class TouchOverlayView extends View {
        private float touchX, touchY;
        private Paint paint;

        public TouchOverlayView(Context context) {
            super(context);
            paint = new Paint();
            paint.setColor(Color.parseColor("#80FF4081"));
            paint.setStyle(Paint.Style.FILL);
        }

        public void updateTouchPosition(MotionEvent ev) {
            touchX = ev.getX();
            touchY = ev.getY();
            invalidate();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawCircle(touchX, touchY, 25, paint);
        }
    }
}

这个方案不需要用户开启任何系统权限,只在你的APP内生效,适合仅需自身APP内调试或演示的场景。

关于你尝试过的悬浮层方案的优化

如果你之前的悬浮层方案存在触摸事件被拦截、显示不流畅等问题,可以结合上面的辅助功能方案:用AccessibilityService监听系统触摸事件,再通过悬浮层更新显示——这样既不会干扰正常的触摸操作,又能实现全局触摸显示的效果。

总结一下:如果需要全局触摸显示,辅助功能是目前普通APP唯一可行的合法方案;如果只需要自身APP内显示,直接在APP内处理触摸事件并绘制即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:32:49