View的事件处理机制

事件处理机制

先来看下dispatchTouchEvent(),该方法的默认会返回值由onTouch()和onTouchEvent()方法确定。

  • 该方法返回true,自己的事件处理流程onTouch/onTouchEvent一定可以收到事件,无论onTouch/onTouchEvent的返回值是什么(告诉上层自己处理事件了,但是activity层的dispatchTouchEvent()依旧能获得事件,因其getWindow().superDispatchTouchEvent(ev)返回true,所以activity的onTouchEvent此时就无法获得到事件了);
//由activity的dispatchTouchEvent
    public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
  //view处理了事件,事件就不会再传给自己的onTouchEvent();
        return true;
    }
    return onTouchEvent(ev);
}     
  • 返回false,不管onTouch/onTouchEvent的返回值是什么都不得事件(告诉上层自己没有处理),以上说的得不到事件不包含Down事件

事件机制非常像上级给下级安排工作,上级获得到一个事件,第一时间是向下级问有没有人要处理该事件,下级的dispatchTouchEvent()方法返回值就是对上级的响应,默认情况下当然处理事件还是要靠动手的,onTouch/onTouchEvent就是手,当下级表示要处理的事件的时候,其也可以根据自身情况,决定是否动手处理该事件,onTouch/onTouchEvent返回true则表示下级动手处理了事件,返回false则表示下级并没有处理该事件,放任其自由,并不是所有的事件都有处理的必要。如果是true则表示我要处理,如果是false则表示我不处理。
当然默认下级的响应是要看自己的手能否处理得了事件,即看onTouch/onTouchEvent的返回值来决定对上级的响应,即决定着dispatchTouchEvent()的返回值。如果下级决定不处理事件,自己的dispatchTouchEvent()返回值为false,事件就会回交上级。

  • 上层的onTouchEvent能否收到事件,要看下层的dispatchTouchEvent()返回值,即下层是否已将事件处理了,不管是下层否处理了事件上层的dispatchTouchEvent()都会收到事件。
  • view的onTouch(),onTouchEvent默认返回值都为false,所以其dispatchTouchEvent()默认返回值也是false,即是不处理事件的。

  • 作为view其dispatchTouchEvent的返回值设置为true后,不管其onTouch/onTouchEvent返回值为什么,其都能接收到事件,处理还是放任不管都会接收到,当然onTouch/onTouchEvent也是有优先级之分的。

这是view的dispatchTouchEvent的核心源码,先判断mOnTouchListener,及onTouch的返回值,
然后在根据会返回值判断是否到达onTouchEvent,再获取onTouchEvent返回值
 if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

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

这是activity的dispatchTouchEvent的源码,通过window将事件交给view,getWindow().superDispatchTouchEvent(ev),
再根据superDispatchTouchEvent(ev)返回值确定事件能否到自身的onTouchEvent();再获取onTouchEvent返回值

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

下面是自己结合API25源码及洋神的分析,来分析下onTouchEvent的源码

private static final int PFLAG_PRESSED = 0x00004000;//长按标示

private static final int PFLAG_PREPRESSED = 0x02000000;//短按标示

private static final int DEFAULT_LONG_PRESS_TIMEOUT = 500;//长按时间检测

private static final int TAP_TIMEOUT = 100;//短按时间检测

Down时:

  • 首先设置标志为PFLAG_PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个TAP_TIMEOUT=100ms后的mPendingCheckForTap;
  • 如果100ms内没有触发up事件,则标志位将清除PFLAG_PREPRESSED,设置为PFLAG_PRESSED,同时发出一个延时为500-100ms的,检测长按的任意任务消息;
  • 如果达到500ms(从down触发起),则触发LongClickListener;

Move时:

  • 主要内容是检测滑动事件是否划出控件,如果已划出:100ms内,直接移除mPendingCheckForTap;100ms后,将标志位PFLAG_PRESSED清除,同时移除长按的检查:removeLongPressCallback();

UP时:

  • 如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;
  • 如果是100ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;
  • 如果是500ms以后,那么有两种情况:
    i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;
    ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;
  • 最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;
    问个问题:
    1、setOnLongClickListener和setOnClickListener是否只能执行一个
    不是的,只要setOnLongClickListener中的onClick返回false,则两个都会执行;返回true则会屏蔽setOnClickListener

setClickable(false)与setEnable(false)异同

有个大家应该都知道的问题就是setClickable(false)的问题,该方法在setOnClickListener()/setOnLongClickListener之前调用是没有效果的,由源码可得知,所以要在之后调用,另外setClickable(false)和setEnable(false)笼统的都是禁止点击的意思。但是setEnable(false)将控件能完全禁用,颜色会变灰色。

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}   

关于viewGroup事件处理机制,见下一篇文章ViewGroup事件分发机制源码分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容