view 事件体系
Posted mangues
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了view 事件体系相关的知识,希望对你有一定的参考价值。
一、view基础
1、view的继承体系
View是android中所有控件的基类,ViewGroup内部包含了许多个控件,即一组View。在Android的设计中,ViewGroup也继承了View,这就意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件,通过这种关系就形成了View树的结构
我们给出一个简略图,可以直观的看到整个体系中常用类的继承结构
2.View的位置关系
view有3中坐标系:屏幕坐标系、视图坐标系、布局坐标系
屏幕坐标系:
以屏幕左上方为(0,0)的坐标体系,X/Y轴的最大值即为物理屏幕分辨率的宽和高;
触摸消息中MotionEvent.getRawX/getRawY取到的就是屏幕坐标值;
视图坐标系:
视图坐标是完全由自身view的宽高决定的坐标体系,理论上他是没有边界的,不受物理屏幕大小限制;
触摸消息中MotionEvent.getX/getY取到的就是视图坐标值;
布局坐标系:
子视图相对于父视图而言的相对屏幕坐标,以父视图的左上角为(0,0),而不关心父视图到底位于屏幕何处。
子视图View的位置主要由它的四个顶点来决定,分别对应于View的四个属性top、left、right,bottom,其中top是左上角纵坐标,left是左上角横坐标,right是右下角横坐标,bottom是有下角纵坐标。需要注意的是,这些坐标都是相对于View的父容器来说的,因此它是一种
相对坐标
图解各种系数获取方法
translationX,translationY
translationX,translationY是左上角相对父容器的偏移量
二、view触摸事件
MotionEvent
在手指接触屏幕后所产生的一系列事件中,典型的事件类型有如下几种:
- ACTION_DOWN一手指刚接触屏幕
- ACTION_MOVE一—手指在屏幕上移动
- ACTION_UP——手机从屏幕上松开的一瞬间
TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离,是一个常量,不同的设备下可能不同。可以用来过滤用户是否滑动。
ViewConfiguration.get(getContext()).getScaledTouchSlop()
View 事件分发机制
触摸事件的分发,其实就是对MotionEvent事件的分发过程,一个MotionEvent产生后系统需要把这个事件传递给一个具体的View。
一、三个重要的方法dispatchTouchEvent,onInterceptTouchEvent和onTouchEvent;
1、dispatchTouchEvent:
用来进行事件的分发
2、onInterceptTouchEvent:
判断本view是否拦截触摸事件,返回true表示拦截,触摸事件不再下内部view传递,直接调用本view的onTouchEvent来处理触摸事件。反之继续向下层view传递。
3、onTouchEvent:
触摸事件实际的处理类,用来处理具体的触摸事件(up,down,move),如果返回true表示该事件被消耗,不在向上层传递该事件。反之上层view会调用onTouchEvent来处理该事件
二、简述一下分发机制:
当一个触摸事件产生后,它的传递过程遵循如下顺序:Activity>Window-View
现在有view1–>view2—>view3 3个view嵌套view1最外层,view3最底层
1、view1获取触摸事件,然后向下层传递,
2、如果中途的view1或者view2没有调用onInterceptTouchEvent进行拦截(返回false),就一直到最下层view3,最后因为view3没有下层view就会调用view3的onTouchEvent方法处理触摸事件,返回true表示已经处理,该事件将被view3消耗。如果false表示不处理,事件将继续回反给view2的onTouchEvent处理,以此类推。
3、如果view1,view2的onInterceptTouchEvent拦截(返回true),会立即调用view1或者view2 的onTouchEvent处理,不会向下传递,处理方式也和正常一样。
借用网上找到的伪代码表示:
@Override
public boolean dispatchTouchEvent(MotionEvent ev)
boolean consume = false;
if(onInterceptTouchEvent(ev))
consume = onTouchEvent(ev);
else
consume = child.dispatchTouchEvent(ev);
return consume;
以下内容来自互联网
关于事件传递的机制,这里给出一些结论,根据这些结论可以更好地理解整个传递机制,如下所示。
(1)同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏慕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数量不定的move事件,最后以up结束
(2)正常情况下,一个事件序列只能被一个Visw拦截且消耗。这一条的原因可以参考(3),因为一旦一个元素拦截了某此事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过特殊手段可以做到,比如一个Vew将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。
(3)某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话),并且它的onInterceprTouchEvent不会再被调用。这条也很好理解,就是说当一个View决定拦截一个事件后,那么系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不用再调用这个View的onInterceptTouchEvent去询问它是否要拦截了。
(4)某个View一旦开始处理事件,如果它不消耗ACTON_DOWN事件(onTouchEvent返回了false),那么同一事件序列中的其他事件都不会再交给它来处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用。意思就是事件一旦交给一个View处理,那么它就必须消耗掉,否则同一事件序列中剩下的事件就不再交给它来处理了,这就好比上级交给程序员一件事,如果这件事没有处理好,短期内上级就不敢再把事情交给这个程序员做了,二者是类似的道理。
(5)如果View不消耗除ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。
(6)ViewGroup默认不拦截任何事件。Android源码中ViewGroup的onInterceptTouchEvent方法默认返回false
(7)View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用。
(8)view的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false),View的longClickable属性默认都为false,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView 的clickable属性默认为false
(9)view 的enable.属性不影响onTouchEvent的默认返回值。哪怕一个View是disable状态的,只要它的clickable或者longclickable有一个为true,那么它的onTouchEvent就返会true。
(10)onclick会发生的前提实际当前的View是可点击的,并且他收到了down和up的事件
(11)事件传递过程是由外到内的,理解就是事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterptTouchEvent方法可以再子元素中干预元素的事件分发过程,但是ACTION_DOWN除外
以上是关于view 事件体系的主要内容,如果未能解决你的问题,请参考以下文章
Android View体系从源码解析View的事件分发机制