Android开发艺术探索学习-View的事件分发机制(一)

来源:互联网 发布:java得到文件路径 编辑:程序博客网 时间:2024/05/20 17:23
    View的事件分发机制是很重要的一个知识点,特别是一些复杂的自定义View,我们需要重写其事件分发的相关方法,以防止事件冲突。与事件分发相关的主要方法如下:
    1⃣️ dispatchTouchEvent(MotionEvent event)
    2⃣️ onInterceptTouchEvent(MotionEvent event)
    3⃣️ onTouchEvent(MotionEvent event)
    接着根据源码来看下以上方法的作用,View类中的dispatchTouchEvent说明如下。
/**     * Pass the touch screen motion event down to the target view, or this     * view if it is the target.     *     * @param event The motion event to be dispatched.     * @return True if the event was handled by the view, false otherwise.     */    public boolean dispatchTouchEvent(MotionEvent event)
    dispatchTouchEvent用于事件的分发,将屏幕的触摸事件往下传递到目标View,或者当前View就是目标View。dispatchTouchEvent的返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,返回true表示消耗当前触摸事件。
    onInterceptTouchEvent是ViewGroup类中的方法,其方法说明如下。
    /**     * 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) {        return false;    }
    onInterceptTouchEvent是在dispatchTouchEvent内部调用,用于判断是否拦截触摸事件,返回true表示拦截当前触摸事件,从上述代码中可以看到,该方法默认返回false,即不拦截触摸事件。
    onTouchEvent用于处理触摸事件,其方法说明如下。
/**     * Implement this method to handle touch screen motion events.     * <p>     * If this method is used to detect click actions, it is recommended that     * the actions be performed by implementing and calling     * {@link #performClick()}. This will ensure consistent system behavior,     * including:     * <ul>     * <li>obeying click sound preferences     * <li>dispatching OnClickListener calls     * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when     * accessibility features are enabled     * </ul>     *     * @param event The motion event.     * @return True if the event was handled, false otherwise.     */    public boolean onTouchEvent(MotionEvent event)
    onTouchEvent在我们自定义View的时候经常遇到,其在dispatchTouchEvent方法中调用,用来处理触摸事件,返回true表示消耗当前触摸事件。在上述说明中提到了一个performClick方法,该方法是用来触发View的点击事件。
    上述三个方法的关系用伪代码描述如下。
public boolean dispatchTouchEvent(MotionEvent event) {boolean consume = false;if (onInterceptTouchEvent(event)) {consume = onTouchEvent(event);} else {consume = child.dispatchTouchEvent(event);}return consume;}
    对于一个跟ViewGroup来说,当触摸事件触发后,首先会传递给自身,接着dispatchTouchEvent方法被调用,然后再判断它是否要拦截当前触摸事件,如果拦截,则调用自身的onTouchEvent方法,消耗触摸事件。如果不拦截,则将事件传递给它的child,接着child的dispatchTouchEvent方法被调用。
    如果View不想被它的父View或者祖先View拦截事件,那么可以通过调用requestDisallowInterceptTouchEvent方法来获取触摸事件,其说明如下。
    /**     * Called when a child does not want this parent and its ancestors to     * intercept touch events with     * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.     *     * <p>This parent should pass this call onto its parents. This parent must obey     * this request for the duration of the touch (that is, only clear the flag     * after this parent has received an up or a cancel.</p>     *      * @param disallowIntercept True if the child does not want the parent to     *            intercept touch events.     */    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
    那么我们会想requestDisallowInterceptTouchEvent和onInterceptTouchEvent哪一个的优先级高呢?这里通过查看ViewGroup类中dispatchTouchEvent方法中有一段代码。
            // Check for interception.            final boolean intercepted;            if (actionMasked == MotionEvent.ACTION_DOWN                    || mFirstTouchTarget != null) {               //disallowIntercept的值可以通过requestDisallowInterceptTouchEvent来设定                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;                //通过下面的代码,就知道requestDisallowInterceptTouchEvent优先级高于onInterceptTouchEvent                if (!disallowIntercept) {                    intercepted = onInterceptTouchEvent(ev);                    ev.setAction(action); // restore action in case it was changed                } else {                    intercepted = false;                }            } else {                // There are no touch targets and this action is not an initial down                // so this view group continues to intercept touches.                intercepted = true;            }
    通过源码,我们可以很深刻地体会,View事件分发机制的工作原理,这对我们以后写高级自定义View很有帮助。



1 0