android基础 -View的事件分发机制
Posted 我叫白小飞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android基础 -View的事件分发机制相关的知识,希望对你有一定的参考价值。
前言
view是我们经常使用的组件,无论是像button、textview还是viewgroup等,都是view的子类。在使用过程中,我们经常碰到的问题就是view的华东冲突,它的解决方法的理论基础就是view的事件分发机制,因此要掌握好view的分发机制是十分重要的。
1 点击事件的传递规则
所谓点击事件的事件分发其实就是对MotionEvent事件的分发过程。当MotionEvent产生后,系统需要将这个事件传递给一个具体的view去做处理,而这个传递的过程将就是分发过程。点击事件的分发过程由三个很重要的方法共同完成:dispathTouchEvent、onInterceptTouchEvent和onTouchEvent,下面我们介绍一下这几个方法:
1.1 public boolean dispatchTouchEvent(MotionEvent ev)
/**
* Called to process touch screen events. You can override this to
* intercept all touch screen events before they are dispatched to the
* window. Be sure to call this implementation for touch screen events
* that should be handled normally.
*
* @param ev The touch screen event.
*
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent ev)
if (ev.getAction() == MotionEvent.ACTION_DOWN)
onUserInteraction();
if (getWindow().superDispatchTouchEvent(ev))
return true;
return onTouchEvent(ev);
注释说明:调用以处理触摸屏事件。您可以覆盖此选项,以便在将所有触摸屏事件发送到窗口之前截获它们。对于应该正常处理的触摸屏事件,一定要调用此实现。 意思是这个事件是触摸事件的起点,这时的事件还未传带窗口,我们可以重写这个方法,做一些自己想要的处理。如果事件能够传递给当前view,那么此方法一定会被调用,返回结果受当前view和onTouchEvent 和 下级view的dispatchTouchEvent方法的影响,表示是否取消当前事件。
1.2 public boolean onInterceptTouchEvent(MotionEvent ev)
/**
* Implement this method to intercept all touch screen motion events. This
* allows you to watch events as they are dispatched to your children, and
* take ownership of the current gesture at any point.
*
* <p>Using this function takes some care, as it has a fairly complicated
* interaction with @link View#onTouchEvent(MotionEvent)
* View.onTouchEvent(MotionEvent), and using it requires implementing
* that method as well as this one in the correct way. Events will be
* received in the following order:
*
* <ol>
* <li> You will receive the down event here.
* <li> The down event will be handled either by a child of this view
* group, or given to your own onTouchEvent() method to handle; this means
* you should implement onTouchEvent() to return true, so you will
* continue to see the rest of the gesture (instead of looking for
* a parent view to handle it). Also, by returning true from
* onTouchEvent(), you will not receive any following
* events in onInterceptTouchEvent() and all touch processing must
* happen in onTouchEvent() like normal.
* <li> For as long as you return false from this function, each following
* event (up to and including the final up) will be delivered first here
* and then to the target's onTouchEvent().
* <li> If you return true from here, you will not receive any
* following events: the target view will receive the same event but
* with the action @link MotionEvent#ACTION_CANCEL, and all further
* events will be delivered to your onTouchEvent() method and no longer
* appear here.
* </ol>
*
* @param ev The motion event being dispatched down the hierarchy.
* @return Return true to steal motion events from the children and have
* them dispatched to this ViewGroup through onTouchEvent().
* The current target will receive an ACTION_CANCEL event, and no further
* messages will be delivered here.
*/
public boolean onInterceptTouchEvent(MotionEvent ev)
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY()))
return true;
return false;
此方法在viewgroup中,大概意思是说:实现此方法来截获所有触摸屏运动事件。这允许您在将事件发送给子类时监听这些事件,并在任何时候拥有当前手势的所有权。 这里可以判断是否拦截某个事件,如果当前view拦截了某个事件,那么在同一事件序列当中,就不会在调用此方法,返回结果表示是否拦截当前事件。
1.3 public boolean onTouchEvent(MotionEvent event)
/**
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen
* outside of your window bounds, where there is no view to receive it.
*
* @param event The touch screen event being processed.
*
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
public boolean onTouchEvent(MotionEvent event)
if (mWindow.shouldCloseOnTouch(this, event))
finish();
return true;
return false;
次方的注释:当触摸屏事件未被其下的任何视图处理时调用。这对于处理发生在窗口边界之外的触摸事件非常有用,因为在那里没有视图可以接收它。 意思是这个拦截方法为最后的拦截方法,在它之前的拦截方法未做任何拦截的时候,才会调用它。
1.4 关系
其实这三个方法之间是有关系的,我们通过一个具体的栗子看一下,具体如下:
我们这样做一个布局
这个点击事件的传递过程如下图所示:
此图侵删!
这个看起来还是很繁琐的,但是我们不关心在灰色部分的事件传递,我们只关心绿色部分的,activity和我们的布局位置的事件传递。
那么这几个位置的事件关系是如何的呢?继续看图:
事件从activity传递到view的过程如上图所示,此图为不做任何处理时的事件传递过程,一路就会返回true,直到返回给顶层window。如果我们给view设置了onClickListener()事件,等于就是在view 中拦截了事件,做了处理,此时就不会view就不会返回false。如下图所示:
view在onClick时处理该事件,所以该view的onTouchEvent方法返回true。
通过上图我们大致了解了事件的传递过程,一下是对于事件传递机制的一些总结:
- 事件序列是指从手指接触屏幕开始到抬起,事件从down -> move -> … -> move -> up等的一系列事件。
- 正常情况下,一个事件序列智能呗一个view拦截消耗,因为如果该view拦截,那么序列里的所有事件会直接交给它处理,因此同一个事件序列不能分别由两个view同时处理。
- 某view如果拦截,那么它的onInterceptTouchEvent() 方法不会再被调用。
- 某一view如果拦截了事件,但是没有做任何处理,并且不消耗ACTION_DOWN事件,那么这个事件序列中的其他事件不会再交给它处理,直接调用父元素的onTouchEvent.
- 如果view不消耗除ACTION_DOWN外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent方法并不会被调用,并且当前view可以持续收到后续事件,最终这些点击事件会传递给activity处理。
- viewgroup默认不拦截任何事件。
- view没有 onInterceptTouchEvent 方法,事件传给它后会调用onTouchEvent方法。
- view的onTouchEvent方法默认都会消耗事件,除非它是不可点击的(clickable 和 longClickable 同时为false)。
- view的enable属性不影响onTouchEvent返回默认值。
- 事件的传递过程是由外向内的。
未完待续。。。。。
以上是关于android基础 -View的事件分发机制的主要内容,如果未能解决你的问题,请参考以下文章