【问题标题】:How can a service listen for touch gestures/events?服务如何监听触摸手势/事件?
【发布时间】:2011-10-06 12:58:30
【问题描述】:

我想知道 SwipePad 和 Wave Launcher 等应用程序如何能够仅通过服务检测触摸手势/事件。这些应用程序能够检测到触摸手势,即使它不在它们自己的 Activity 中。我查看了整个互联网,但没有找到他们如何做到这一点。

我的主要问题是服务如何监听触摸访客/事件,就像常规 Activity 可能接收 MotionEvents 一样,即使它可能不在原始 Activity 或上下文中。我本质上是在尝试构建一个应用程序,该应用程序将识别用户的特定触摸手势,而不管哪个 Activity 在顶部,并在识别该手势时执行某些操作。触摸识别将是作为服务在后台运行的线程。

【问题讨论】:

  • SwipePad 看起来可能只是为可触摸区域而不是整个屏幕使用透明的系统警报窗口。
  • 我有同样的问题,我从三天前开始尝试解决。问题是我需要在前台服务类下收集点击的触摸事件数据。你能帮我在这里回答我的问题吗:stackoverflow.com/questions/65405516/…
  • 这个问题是很久以前的了。从那以后,我了解到我自己的问题的前提不是很好。服务本身不能接收任何触摸事件,因为它不是 UI 组件。有一些 hacky 方法涉及使用系统警报窗口标志来创建由服务启动的视图。但 Android 不鼓励这种方法,并且随着 Android 11 中 Bubbles API 的引入而有效地停止使用。

标签: android events service touch gesture


【解决方案1】:

我遇到了同样的问题,我终于想通了!感谢这篇文章:Creating a system overlay window (always on top)。您需要使用警报窗口而不是覆盖(这也意味着您可以在 Andoid ICS 中使用它):

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);

然后以这种方式附加一个 GestureListener:

GestureDetector gestureDetector = new GestureDetector(this, new AwesomeGestureListener());
View.OnTouchListener gestureListener = new View.OnTouchListener() {
      public boolean onTouch(View v, MotionEvent event) {
            return gestureDetector.onTouchEvent(event);
      }
};

overlayView.setOnTouchListener(gestureListener);

耶!

【讨论】:

  • 这会影响用户点击他们认为正在查看的活动中的按钮的能力
  • @ChuckKelly 是的,它会干扰
  • 我正在尝试监听来自服务的点击,服务附加了一个 Activity。该活动照常使用,但服务会继续记录水龙头。使用 TYPE_SYSTEM_OVERLAY 时,它在 Android 2.3 上工作得很好,但在 4.1 上不会调用 onTouchEvent。使用 TYPE_SYSTEM_ALERT 时,onTouchEvent 被调用,但触摸事件未传递给活动,并且它变得无响应。请帮忙
  • 这适用于我在 Android 4.3 中,但触摸事件被服务消耗,导致手机大部分无法使用。
  • 不是一个好的答案,因为它会阻止所有其他正在运行的应用程序。
【解决方案2】:

有趣的问题。我不知道他们是怎么做到的,我发现谷歌群组帖子告诉我没有全局触摸监听器。不过我有个主意……

我发现this post 有人成功显示来自服务的弹出窗口。如果我想让弹出窗口透明且全屏,我确信我可以捕捉到触摸,因为我可以设置touch interceptor

编辑:请在尝试时报告结果,知道这是否有效会很有趣...

【讨论】:

  • 这似乎是一个有趣的方法。我以前见过 PopupWindows 用于类似的事情,但我必须试一试。目前,我对从服务中获取触摸事件的难度感到沮丧。我还需要一段时间才能有机会尝试一下,但我会告诉你进展如何!
  • 我刚试过这个,我什至无法显示警报对话框。 show 方法抛出了 BadTokenException
  • 异常信息为Unable to add window -- token null is not valid; is your activity running?
  • 看起来该帖子可以访问输入视图,这可能是他们让它工作的原因。
【解决方案3】:

我测试了所有可能的解决方案,但没有任何效果,有些没有触发触摸事件,他们冻结了屏幕。

所以我做了一些逆向工程,现在发布了可行的解决方案

  WindowManager.LayoutParams params = new android.view.WindowManager.LayoutParams(0, 0, 0, 0, 2003, 0x40028, -3);
        View mView = new View(this);

        mView.setOnTouchListener(this);

        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mView, params);

【讨论】:

  • 这似乎只适用于第一个视图/活动。不适用于随后创建的活动。我收到以下警告:W/ViewRootImpl:由于没有窗口焦点而丢弃事件:MotionEvent { action=ACTION_OUTSIDE, actionButton=0, id[0]=0, x[0]=0.0, y[0]=0.0, toolType[ 0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=220247332, downTime=220247332, deviceId=4, source=0x1002 }
  • 你拯救了我的一天!
【解决方案4】:

我在 API 21 和 nexus 5 上对此进行了测试,当系统触发触摸事件时,我能够从服务中记录下来。但是,MotionEvent 对象内的数据(例如坐标)在似乎是我的应用程序包的外部时返回 0.0。

两个来源 integration, permission

我很好奇为什么 event.getX()、event.getY() 和 event.getPressure() 在不在服务所在应用的活动中时返回 0.0。

编辑

该方案不会因为

而阻止其他监听器接收到服务捕获的触摸事件
public boolean onTouch(View v, MotionEvent e){
    Log.d(LOG_TAG, "Touch event captured");
    return false;      

通过返回 false,它允许其他侦听器接收事件。

编辑2

显然,如果当前活动和监听器不共享UID,OS中的输入调度程序会将坐标和压力设置为0,这里是source

【讨论】:

  • 您是否找到解决此问题的方法?喜欢动态更改 UID?
  • 除非你有自己的操作系统,否则我认为这是不可能的。也许如果该服务被注册为系统服务?我的意思是值在那里,但操作系统会删除感兴趣的值并将触摸事件传播到任何其他侦听器。
【解决方案5】:

我已经搜索了许多 SO 线程,但出于安全原因,我认为这对系统上的所有包都不可行。当然,它适用于您自己的应用程序。

窗口管理器

 WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);

浮动/叠加布局

floatyView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate
            (R.layout.floating_view, null);

    floatyView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.d(TAG, "event.x " + event.getX());
            Log.d(TAG, "event.y " + event.getY());
            if (event.getAction() == MotionEvent.ACTION_UP) {
                v.performClick();
            }
            return false;
        }
    });

布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:background="@android:color/transparent"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

【讨论】:

    猜你喜欢
    • 2019-01-27
    • 1970-01-01
    • 2015-04-19
    • 2016-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    相关资源
    最近更新 更多