Android touch事件一种解释

研究了一下android的touch事件,从doc到google,算是有了一些初步的理解。以下是经过消化的个人理解,有可能与事实不符,欢迎指正。

  首先,来了解一下android的事件机制。android的基本元事件我猜应该有5种,理由是MotionEvent类里有5个事件常量,分别是 ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL和ACTION_OUTSIDE。其中 DOWN/MOVE/UP是人为触发的,CANCEL是系统触发,至于OUTSIDE,doc里写是当事件发生在UI元素之外的时候出发,但实际上我还从 来没有成功触发过这个事件。也许你会觉得按钮按下,然后移出按钮边界会触发这个事件,但很遗憾实际上不会。
  也就是说,其实我们能够用到的元事件只有三种,DOWN/MOVE/UP,就像三原色调配出多彩的世界,android其余的复杂事件都是由这三个元 事件组合而成的。比如scroll(滚动)/fling(就是DOWN然后快速的MOVE一小段距离然后UP)/longpress(长按) /singletapup(单击)等等……
  那么系统是怎么响应这些事件的呢?响应事件的方法有几种。
  最简单也是最常用的是实现一个OnClickListener的接口,然后用view的setOnClickListener方法绑定这个接口,就可 以处理view的单击事件。这种方法简便易行,但是只能处理单击这一种事件。实际上,click这个事件是顺序触发ACTION_DOWN和 ACTION_UP两个事件组成的,中间可以存在ACTION_MOVE,但不能MOVE出view的边界,不然即使再MOVE回来也不能触发click 事件了。OnClickListener接口的onClick方法是在click事件的ACTION_UP的timing执行。onClick方法只有一 个参数,就是被单击的View,这种方法是不能获取单击事件的,也就无法获取单击点的坐标等属性。
  第二种略微复杂一点的事件处理方法是用OnTouchListener接口而不是OnClickListener,实现方法跟上面一种差不多,实现这 个接口的onTouch(View v, MotionEvent e)方法之后,就可以响应touch的元事件了,注意,是元事件!也就是说,一次简单的click事件会在DOWN和UP的时候分别执行onTouch方 法各一次(如果是scroll一下会触发一次DOWN和很多次的MOVE)。同时,由于有了MotionEvent的参数,我们可以用 e.getAction()来获取元事件类型或者e.getX()/e.getY()来获取事件发生点的坐标,以及许多其他的事件属性。这样,能干的事情 就多那么一点点了。这个方法还有一个boolean的返回值,至于这个返回值是做什么用的,一会儿再交代。
  第三种是连接口都用不着,而是直接覆写一个现成的方法view.onTouchEvent(MotionEvent e),这个方法和onTouch方法的不同,直观上来讲,没有view参数了。那这个方法不就没有onTouch方法强大了?那要它有啥用呢?这就要从android的事件传播机制讲起了。
  android的事件传播机制跟网页W3C的标准有一点点类似,都是一个事件产生之后经过一个由上到下的捕捉过程再经过一个由下到上的冒泡过程。但是 不同的是,android事件在传播过程中的某一层如果被消费了,就会终止传播。也就是说,发生在一个按钮上的ACTION_DOWN事件实际上是先发生 在他的父父父父控件(某一个ViewGroup)上然后再层层传过来的,按钮如果消费这个事件,那么传播终止(如果他爸爸消费了这个事件,那么按钮就悲催 地根本获取不到这个事件),如果它不消费这个事件,那么这个事件又会冒泡回去,过程大致是这样。
  具体来讲,android的事件产生以后,捕捉过程是由ViewGroup.onIntercepTouchEvent(MotionEvent e)传递的,即产生之后,由上向下,依次传递给子ViewGroup的onInterceptTouchEvent方法。这个方法有个boolean的返 回值,false表示本人不消费这个事件,这个事件继续传给我儿子。true表示本人留下这个事件啦,捕捉过程到此为止,不传给儿子了。儿子的 onInterceptTouchEvent方法就不会再执行了。注意,这个方法只有在ViewGroup里有,而最孙子的View里是没有的,因为到 View为止,捕捉过程一定会结束。
  那么,捕捉过程终止过后,冒泡过程是由谁来处理的呢?答案就是onTouchEvent方法。当捕捉过程达到最孙子的View(我知道应该叫叶子 View……),或者某一层的ViewGroup的onInterceptTouchEvent返回值为true的时候,该View或者该 ViewGroup的onTouchEvent方法就会执行,这个方法同样会返回一个boolean,false代表我不消费这个事件,这个事件冒泡回去 孝敬我爸爸,true表示我消费啦,事件传播就此终止。而冒泡回去的事件,就由上一层的onTouchEvent来处理。这就是说,网页的事件是捕捉冒泡 走一条线,并且默认不会被消费而阻断的(关于网页事件的流程,感谢stauren提供技术支持),而android的事件则是捕捉冒泡分两个方法线来完 成,一旦某一个节点消费了这个事件,事件就停止传播了。
  写到这里,有人可能会发现一个问题。什么叫“事件被消费了”?不就是事件被处理了么?如果我把事件处理的方法写在 onInterceptTouchEvent/onTouchEvent里,但是返回值却是false,那么不就做到,既处理了事件,又没有停止事件传 播,不就跟网页一样了么?(这种需求实际工作中非常有用!)
  愿望是好的,但实际操作一下,你就会发现,除了ACTION_DOWN事件以外,其余的事件你都只有返回值是true的那个方法才能捕捉的到。这是为 什么呢?为什么DOWN事件又可以呢?这是因为,android的事件传播只有在第一个事件发生的时候(所有事件第一个发生都是DOWN,你得先把手按上 去嘛……)按前述顺序进行一次,找到那个返回值是true的方法,然后,所有后续事件都会直接传给那个View,不再经历中间的传播过程!所以,中间路径 上的那些返回false的方法,只能捕捉到每次的第一个DOWN事件,而后续的MOVE和UP事件就捕捉不到了。
  那么有没有办法能够让消费之后传播过程继续呢?有,只要你在返回true的方法里人为调用上一层的onTouchEvent或者下一层的onInterceptTouchEvent,传播不就进行下去了么?
  最后,处理复杂的三元事件的组合事件,android提供了一个GestureDetector类,实现 GestureDetector.GestureListener接口之后(就是各种onLongPress/onFling/onScroll等方法) 并实例化一个GestureDetector对象之后,就可以在本来要处理事件的onTouchEvent方法里人工调用这个detector实例自带的 onTouchEvent方法,改由detector来处理事件,于是各种复杂事件就能被识别和处理了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要让一个apk在Android系统中置顶并不被其他应用所覆盖,可以使用以下方法: 1. 使用系统权限 你可以请求系统权限,使你的应用在最前面运行。具体方法是在AndroidManifest.xml文件中添加以下权限: ``` <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> ``` 然后,在你的代码中调用以下方法: ``` WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT); WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); View view = View.inflate(this, R.layout.your_layout, null); wm.addView(view, params); ``` 这样,你的应用就会在最前面运行,并且不会被其他应用所覆盖。需要注意的是,这种方法需要用户授权,而且在Android 6.0及以上版本中,用户可以随时关闭此权限。 2. 使用Activity的属性 在你的Activity的定义中添加以下属性: ``` android:windowIsTranslucent="true" android:windowBackground="@android:color/transparent" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" ``` 这样,你的Activity就会变成一个透明的窗口,其他应用就不会覆盖它了。需要注意的是,这种方法只适用于Activity,而且在一些低版本的Android系统中可能会出现不兼容的问题。 以上是两种在Android系统中让应用置顶的方法,你可以根据自己的需求选择适合自己的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值