View源码-Touch事件

在Android-27中查看源码:

首先我们来查看单个View的触摸事件的处理,在View的dispatchTouchEvent方法中看看源码是如何处理的。


public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don‘t have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn‘t want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

dispatchTouchEvent处理过程如下:

  1. 如果设置了OnTouchListener和enabled为true,并且onTouch返回为true,即该View在OnTouchListener的onTouch方法中处理了触摸事件。
  2. 如果onTouchEvent返回为true,即该View在onTouchEvent中处理了触摸事件。
  3. 如果触摸事件在该View中得到处理则返回true,否则返回false(即该View对触摸事件不予处理)。

接下来查看onTouchEvent方法的源码:


    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn‘t respond to them.
            return clickable;
        }
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don‘t have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                        }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

                case MotionEvent.ACTION_DOWN:
                    if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                        mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                    }
                    mHasPerformedLongPress = false;

                    if (!clickable) {
                        checkForLongClick(0, x, y);
                        break;
                    }

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we‘re inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);
                        checkForLongClick(0, x, y);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if (clickable) {
                        drawableHotspotChanged(x, y);
                    }

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        // Remove any future long press/tap checks
                        removeTapCallback();
                        removeLongPressCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            setPressed(false);
                        }
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    }
                    break;
            }

            return true;
        }

        return false;
    }

onTouchEvent里面的处理如下:

  1. 如果setEnabled(false),则返回false,即该View对触摸事件不予处理。
  2. 如果设置了TouchDelegate,并且TouchDelegate的onTouchEvent返回true,则返回true,即由TouchDelegate中的onTouchEvent方法处理了触摸事件。否则继续向下。
  3. 如果该View不可点击并且(viewFlags & TOOLTIP) != TOOLTIP(即当停留或长按在该view时不可显示提示框),则返回false,即该View对触摸事件不予处理。否则继续向下。
  4. 根据触摸事件的分类进行处理:
    1. MotionEvent.ACTION_DOWN:

      1. 不可点击或者不在可滑动的容器内:如果LONG_CLICKABLE为true则执行OnLongClickListener的onLongClick方法;或者设置了viewFlags为TOOLTIP,则执行showTooltip(显示提示框)。然后返回true,即该View处理了触摸事件,否则继续向下走。
    2. MotionEvent.ACTION_MOVE:在手指移动的过程中判断是否仍在当前view的范围。
    3. MotionEvent.ACTION_UP:
      1. 显示了提示框的话则隐藏;
      2. 如果不可点击,则移除长按事件的检测,返回true,否则继续向下走。
      3. 当前是点击事件,所以移除长按事件的检测。然后执行performClick方法,返回true,即该View处理了触摸事件。

关于ViewGroup源码中的Touch事件,可以参考文章:ViewGroup源码-Touch事件

原文地址:https://www.cnblogs.com/qixidi/p/10085379.html

时间: 2024-07-30 21:28:59

View源码-Touch事件的相关文章

CBV-2-CBV流程-View源码解析

CBV是基于反射实现根据请求方式不同,执行不同的方法. 请求流程:view源码解析 1.urls.py :请求一定来执行视图下的as_view方法.也可以直接点击as_view()来找源码. 2.views.py 视图内没有as_view方法,则找父级的as_view方法. 3.源码:as_view返回自己下面的view方法,as_view执行了自己view方法,返回值是dispatch方法. 4,dispatch方法判断请求方式. 5,所以请求已经来,第一步先执行的都是dispatch方法.

CBV-2-CBV流程-view源码解析-面向对象-继承

CBV-2-CBV流程-view源码解析-面向对象-继承 CBV,基于反射实现根据请求方式不同,执行不同的方法. 请求流程:view源码解析 1.urls.py :请求一定来执行视图下的as_view方法. 2.views.py 视图内没有as_view方法,则找父级的as_view方法. 3.源码:as_view返回自己下面的view方法 4.as_view执行了自己view方法,放回值是dispatch方法. 5.dispatch方法判断请求方式. 6.所以请求已经来,第一步先执行的都是di

Spark2.1.0之源码分析——事件总线

阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析--事件总线>.<Spark2.1.0事件总线分析--ListenerBus的继承体系>及<Spark2.1.0事件总线分析--SparkListenerBus详解>几篇文章的内容. LiveListenerBus继承了SparkListenerBus,并实现了将事件异步投递给监听器,达到实时刷新UI界面数据的效果.LiveListenerBus主要由以下部分组成: eventQueue:是SparkLis

REST、DRF(View源码解读、APIView源码解读)

一.REST 1.什么是编程? 数据结构和算法的结合 2.什么是REST? 首先我们回顾下我们之前的图书管理系统,我们设计了这样的URL,如下: 127.0.0.1:9001/books/ 127.0.0.1:9001/get_all_books/ 访问所有的数据 127.0.0.1:9001/books/{id}/ 127.0.0.1:9001/books/{id}?method=get 访问单条数据 127.0.0.1:9001/books/add/ 127.0.0.1:9001/books

【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑][下文简称(五),请先阅读完(五)再阅读本文],我们通过示例和log来分析了Android的事件分发机制.这些,我们只是看到了现象,如果要进一步了解事件分发机制,这是不够的,我们还需要透过现象看本质,去研究研究源码.本文将从源码(基

jQuery源码解读-事件分析

最原始的事件注册 addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法.先看下addEventListener方法签名: element.addEventListener(event, function, useCapture) event:事件名,例如“click”,这里要提醒的一点是不要加前缀“on”;    function:事件触发时执行的函数;    userCapture:默认为false,表示event事件在冒泡阶段触发.如果设置为true,则

jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

转:http://www.cnblogs.com/aaronjs/p/3440647.html?winzoom=1 事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行 为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分 网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新

【jQuery源码】事件存储结构

a. jQuery事件原型——Dean Edwards的跨浏览器AddEvent()设计 源码解读   重新梳理一下数据结构,使用一个例子 <input type="text" id="chua" onClick="f0();"> function f0(){...} function f1(){...} function f2(){...} function f3(){...} var dom = document.getEleme

zepto源码分析------事件篇

说说zepto.js的源码.今天先分析事件,我比较懒,前面的基础函数就不分析了.直接按模块写就可以了. 在分析之前先简单的说一下zepto的大致结构 var Zepto = (function{})() // 事件模块 (function($){})(zepto) //ajax模块 (function($){})(zepto) // form事件 (function($){})(zepto) 大致结构就是这样了;其实里面的函数也挺简单的,我们都知道zepto这个框架是专为移动端开发,可谓是短小精