首页 > 系统 > Android > 正文

Android中View的Clickable和Enabled的区别与原理

2019-11-09 17:27:27
字体:
来源:转载
供稿:网友

View的setClickable(setLongClickable方法与此类似)和setEnabled方法相信大家都用过,根据方法名来理解就是设置View可不可以点击以及可不可用,但是可不可以点击以及可不可用具体表现出来又是怎样呢?他们之间又有什么区别呢?在看源码之前可能心里多多少少有点迷糊,那么,我们接下来到源码里一探究竟。阅读本篇文章需要了解基本的事件分发机制,不了解的可以先去郭霖大神的博客学习:Android事件分发机制完全解析,带你从源码的角度彻底理解(上),另外本篇源码基于Android7.0,主要分析不可用以及不可点击的情况以便跟正常情况做对比。

在这里我先给出几个结论:

当View是不可用的时候,通过setOnTouchListener设置的OnTouchListener中的onTouch方法将不会执行。当View是不可用的时候,onTouchEvent会被执行,但不会执行实质的逻辑,比如onClick、onLongClick等方法不会被执行到。此时onTouchEvent的返回值由该View能不能点击(包括长按和短按等点击状态)来决定。可以点击时返回true,否则返回false。当View是不可点击的时候,除非调用过View的setTouchDelegate方法,否则onTouchEvent必定会返回false,具体的逻辑,例如onClick、onLongClick等不会被调用。

setClickable和setEnabled都是对mPRivateFlags做操作,判断View是否可点击以及是否可用也是基于mPrivateFlags做判断。当mPrivateFlags & CLICKABLE == CLICKABLE时代表控件是可点击的,当mViewFlags & ENABLED_MASK == ENABLED代表控件是可用的。

我们接下来就看一下View的dispatchTouchEvent方法是如何处理可点击与可用状态的。

public boolean dispatchTouchEvent(MotionEvent event) { ...... boolean result = false; ...... 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; } } ...... return result; }

上面只贴了相关的核心代码。 首先我们可以从第10行的判断语句中看出,当View是DISABLED时,将不会去执行handleScrollBarDragging(event)方法,第11行的result = true也不会被执行。

同理,在第16行的判断语句中有一个条件(mViewFlags & ENABLED_MASK) == ENABLED,且紧接着在它后面执行的语句是li.mOnTouchListener.onTouch(this, event),也就是说当View不是ENABLED的时候将不会执行到我们通过setOnTouchListener设置进去的mOnTouchListener的onTouch方法,此时18行的result = true也不会被执行。

如上所示,当View不是ENABLED的时候,第11行和第18行的result = true将会执行不到,而result的默认值是false,所以将会执行到第21行中的onTouchEvent(event),即dispatchTouchEvent的返回值result的将由onTouchEvent(event)方法的返回值决定,换句话说,事件是否交由这个View来处理将由该View的onTouchEvent(event)的返回值决定。

那么事不宜迟,我们来看下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(); if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { case MotionEvent.ACTION_UP: ...... break; case MotionEvent.ACTION_DOWN: ...... break; case MotionEvent.ACTION_CANCEL: ...... break; case MotionEvent.ACTION_MOVE: ...... break; } return true; } return false; }

由第7行和第13行可知,当View是DISABLED的时候将会直接return出去,而retrun的值是由View是否可以点击来决定的。当View是可点击的时候就会返回 true,也就是说事件会一直分发到这个View中,但是实质上并没处理实质逻辑,比如onClick、onLongClick等方法不会执行。而View完全不能点击的时候将返回false,即事件不会分发到这个View中。

而当View不是DISABLED的时候代码将会执行到第23行,当View可点击的时候就会执行if块区中的代码,onClick和onLongClick等方法将会执行,并且会返回true。不可点击时则返回false,且不执行具体逻辑。

分析到此结束,如有错漏,欢迎提出。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表