View的事件传递源码详细解析
来源:互联网 发布:mac ifconfig 编辑:程序博客网 时间:2024/06/05 19:06
View的事件传递源码解析
View是Android中最小的基本的的UI的控件,当触摸事件传递到它的时候基本上就是最后一个处理事件的控件;它不同于ViewGroup,它没有其他的子类;并且View的事件传递的源代码是最好掌握的,所以在这里认真分析源代码,学习记录一下;源代码使用android2.2的源码,因为这个版本的View事件源代码最简单,最直接;很利于学习,而5.0的看了半天晕晕的,android从2.2到5.0的源代码逻辑机构不能变化太大,只能是重构优化很多了;所以从2.2开始了~~
dispatchTouchEvent
一个触摸事件传递到一个UI控件的时候,都是先传递到这个的UI控件的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) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
上面的源码注释是说:往下传递触摸屏幕事件到目标View,或者是传给这个View如果这个View是最终目标的话;
参数意思是要被分发下去的事件行为(就是这个触摸事件行为)
返回 如果这个事件被View处理了返回true ,否则返回false
第9行是一个if条件判断里面有三个条件,并且是并集,要全部成立就进入括号里面返回的是true;不会再往下执行代码,否则继续往下执行;
第13行是执行onTouchEvent方法:
先看看if的三个条件:
1:第一个条件 mOnTouchListener != null
mOnTouchListener是什么对象?它是触摸监听OnTouchListener的某个实现类的对象(OnTouchListener是抽象接口),是开发者我们自己注入的,也可从源代码里找到;
/** * Register a callback to be invoked when a touch event is sent to this view. * @param l the touch listener to attach to this view */ public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; }从这里,我们可以看到mOnTouchListener被赋值了,如果我们没有设置setOnTouchListener那么mOnTouchListener就是null了,if条件就不成立了
我们设置setOnTouchListener之后,就会就行第二个条件的判断
2:(mViewFlags & ENABLED_MASK) == ENABLED
这个条件的意思是当前这个View是不是可用的,能够使用;
单独看每一个位运算符,也是心里不是很踏实,到底是不是,其实从源码里也是可以确认的:
/** * Returns the enabled status for this view. The interpretation of the * enabled state varies by subclass. * * @return True if this view is enabled, false otherwise. */ @ViewDebug.ExportedProperty public boolean isEnabled() { return (mViewFlags & ENABLED_MASK) == ENABLED; }这个方法使View判断View是不是可用的,里面的条件就是(mViewFlags & ENABLED_MASK) == ENABLED,可用返回true,不可用返回false
3:mOnTouchListener.onTouch(this, event)
这个条件就是子类实现类调用子类实现的接口方法,虽然引用还是父类OnTouchListener(这里是多态的小运用)
我们看看OnTouchListener的接口,里面只有一个onTouch方法,
/** * Interface definition for a callback to be invoked when a touch event is * dispatched to this view. The callback will be invoked before the touch * event is given to the view. */ public interface OnTouchListener { /** * Called when a touch event is dispatched to a view. This allows listeners to * get a chance to respond before the target view. * * @param v The view the touch event has been dispatched to. * @param event The MotionEvent object containing full information about * the event. * @return True if the listener has consumed the event, false otherwise. */ boolean onTouch(View v, MotionEvent event); }现在想想,如果子类实现了这个接口,不就得实现接口里面的方法吗?,而恰好这个方法返回的是一个布尔类型的变量值;
如果我们实现了OnTouchListener接口,并且方法返回值为true,那么这个条件就成立了;
总结一个 这个if的小条件 成立有三个条件:
第一:设置了setOnTouchListener ,那么mOnTouchListener就不为null
第二:这个View本身是可以使用的
第三:实现的mOnTouchListener对象中的onTouch方法返回true ,
否则 执行下面的 onTouchEvent方法
onTouchEvent
下面在逐步分析onTouchEvent方法的内部执行:
这个方法比较长,这样也好,就不用我们找来找去了^-^,贴一下代码
/** * Implement this method to handle touch screen motion events. * * @param event The motion event. * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { // 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)); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; if ((mPrivateFlags & 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 (!mHasPerformedLongPress) { // 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) { mPrivateFlags |= PRESSED; refreshDrawableState(); postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPrivateFlags |= PREPRESSED; mHasPerformedLongPress = false; postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break; case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); removeTapCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = mTouchSlop; if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } } break; } return true; } return false; }
(viewFlags & ENABLED_MASK) == DISABLED)
这段代码我们按照执行步骤一段段来分析吧,先看下面这段
final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { // 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)); }
对于这段代码 主要看if中的条件 (viewFlags & ENABLED_MASK) == DISABLED 这句意思是判断这个View是不是可用的 ,首先源代码里面
/** * This view is disabled. Intrepretation varies by subclass. Use with * ENABLED_MASK when calling setFlags. {@hide} */static final int DISABLED = 0x00000020;/** * Mask for use with setFlags indicating bits used for indicating whether * this view is enabled {@hide} */static final int ENABLED_MASK = 0x00000020;
DISABLED和ENABLED_MASK都是不可变的final型int值;这里使用了位运算& ,转为二进制相同位置都是1的时候才是1
DISABLED 从16进制转化为2进制 是 0000 0000 0000 0000 0000 0000 0010 0000
ENABLED_MASK 转化为2进制 是 0000 0000 0000 0000 0000 0000 0010 0000
其实16进制到2进制 转化很简单 16进制的每一位自己可以转化成2进制 ,如果你还是很蒙,看看这篇介绍就懂了 二进制位运算积累备忘
上面viewFlags & ENABLED_MASK的位运算之后,只有2个结果 要么是0x00000020 就是 DISABLED ,要么是 0x0000000 就是 ENABLED,
/** * This view is enabled. Intrepretation varies by subclass. Use with * ENABLED_MASK when calling setFlags. {@hide} */static final int ENABLED = 0x00000000;
如果不可用的话,if条件成立 会返回 (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
这的意思可以点击或可以长按点击都会返回true ;至于二进制的位运算 和上面分析可不可用是一样一样的;另外这两个条件分别用于判断这个View是不是可点击和是不是可长点击 在源码中也是可以找到的,再贴一下源代码,这样看的更明白了:
/** * Indicates whether this view reacts to click events or not. * * @return true if the view is clickable, false otherwise * * @see #setClickable(boolean) * @attr ref android.R.styleable#View_clickable */ @ViewDebug.ExportedProperty public boolean isClickable() { return (mViewFlags & CLICKABLE) == CLICKABLE; } /** * Indicates whether this view reacts to long click events or not. * * @return true if the view is long clickable, false otherwise * * @see #setLongClickable(boolean) * @attr ref android.R.styleable#View_longClickable */ public boolean isLongClickable() { return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE; }
这里小结一下:如果一个View是不可用,但是只要它的可以点击或者是可长按点击的,它都返回true,也就是消耗了这次事件;
mTouchDelegate != null
接着再往下看 下一段源代码
if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } }
这就源代码的意思是,这个View有没有设置触摸代理事件 ,如果mTouchDelegate不为null的话,就会进入这个条件内部
mTouchDelegate是怎么来的,还是 从源代码里面找
/** * Sets the TouchDelegate for this View. */ public void setTouchDelegate(TouchDelegate delegate) { mTouchDelegate = delegate; }
使使用者在这里注入的,没有注入的话mTouchDeleagte这个对象就是null,上面的条件不成立了
public class TouchDelegate {
我们可以重写TouchDelegate里面的onTouchEvent方法,也可以不重写,因为这个类本身自己有实现onTouchEvent方法,
这个TouchDelegate唯一的构造如下:
/** * Constructor * * @param bounds Bounds in local coordinates of the containing view that should be mapped to * the delegate view * @param delegateView The view that should receive motion events */ public TouchDelegate(Rect bounds, View delegateView) { mBounds = bounds; mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop(); mSlopBounds = new Rect(bounds); mSlopBounds.inset(-mSlop, -mSlop); mDelegateView = delegateView; }
看注释的意思是:包含这个View的当前坐标的边界范围(尺寸)应该被映射到这个delegateView,,这个delegateView应该受到这个触摸事件,说白了,就是把当前View的触摸事件叫另一个View来处理,这里我们明白就可以了;不再往下深究了,不过View的子类中好像很少设置这个代理的,我们私下可以自己弄个代理看看效果;
((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
再往下看下一段源代码,又是一个大长段的 if条件语句,里面包含了一个switch语句,也就说,这要是可点击或者是可长按点击,这个结果返回true,事件一定是这个View消耗处理的,为了方便浏览,再贴一下源代码吧
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; if ((mPrivateFlags & 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 (!mHasPerformedLongPress) { // 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) { mPrivateFlags |= PRESSED; refreshDrawableState(); postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPrivateFlags |= PREPRESSED; mHasPerformedLongPress = false; postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break; case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); removeTapCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = mTouchSlop; if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } } break; } return true; }可以看到return true; 不管你是哪一个行为 只要进入这个if,统统返回true;代表事件被这个View消费了;
现在已经是onTouchEvent方法的最后了,对于switch情形中的各种case ,我们一个一个啃,逐步分析,就按照
按下(MotionEvent.ACTION_DOWN) ,
移动(MotionEvent.ACTION_MOVE),
取消了(MotionEvent.ACTION_CANCEL),
抬起(MotionEvent.ACTION_UP) 这个事件的逻辑进行
MotionEvent.ACTION_DOWN
首先看按下:
case MotionEvent.ACTION_DOWN: if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPrivateFlags |= PREPRESSED; mHasPerformedLongPress = false; postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break;
代码第2行 mPengdingCheckForTap是一个Runnable类型的对象,如果是null
代码第3行就会创建一个对象
代码第4行 是一个私有的flag标记变量mPrivateFlags ,mPrivateFlags |= PREPRESSED;这句的意思是标记为点击按下了的意思
代码第5行 是 mHasPerformedLongPress是一个布尔类型的变量,值为false,表示 还没有执行长按;
代码第6行是将任务mPengingCheckForTap加入到消息队列里面延迟执行 ,至于延迟多久 ViewConfiguration.getTapTimeout()获取的时间,源码中是115毫秒,这里是单击的时间,超过了115毫秒单击时间限定之后就执行里面的Runnable任务;
对于第4行代码,可能有疑惑到底是干嘛用的,这里我解释一下 ,View中有很多状态 ,比如 可不可以单击 可不可以双击 ,可以可以长按, 可不可以由焦点;其实每一种这些状态都对应2种 要么可以,要么不可以 反应到代码中控制的是2进制的某一个为是1和0, 1表示true 0表示不可以,说白了是通过二进制位运算来控制,因为2进制每一位是0或1,这样说可能还是有一点不清楚,下面举个例子演示一下 :
比如 mPrivateFlags |= PREPRESSED;
假设 mPrivateFlags 是任意一个值 转化成二进制以后 是 如下 :aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa
而从源码中可以知道PREPRESSED是一个常量值0x02000000 :0000 0010 0000 0000 0000 0000 0000 0000
mPrivateFlags |= PREPRESSED; 的结果是 : :aaaa aa1a aaaa aaaa aaaa aaaa aaaa aaaa
可以看到mPrivateFlags的结果中除了1的那个位置其他位置都是保持不变的 ,这里变成了1 ,可以理解为 点击按下了的意思
~PREPRESSED :1111 1101 1111 1111 1111 1111 1111 1111
mPrivateFlags &= ~PREPRESSED; :aaaa aa0a aaaa aaaa aaaa aaaa aaaa aaaa
可以看到mPrivateFlags的结果中除了0的那个位置其他位置都是保持不变的 ,这里变成了10,可以理解为取消了点击按下了的意思
变量 mPrivateFlags 当 |PREPRESSED之后相应为值的值置为1,其他位置都是不变的,其实也不不用关心,因为只要这个位置是1,我们就知道它是的按下条件成立了
变量 mPrivateFlags 当 &~PREPRESSED之后相应为值的值置为0,其他位置都是不变的,其实也不不用关心,因为只要这个位置是0,我们就知道它是的按下条件不成立了
接下来,我们在看看mPendingCheckForTap中实现的run方法:
private final class CheckForTap implements Runnable { public void run() { mPrivateFlags &= ~PREPRESSED; mPrivateFlags |= PRESSED; refreshDrawableState(); if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { postCheckForLongClick(ViewConfiguration.getTapTimeout()); } } }
第3行取消了点击按下行为标记(上面已经解释了) ,因为按下事件超过了115毫秒,之后就不能再当单击按下行为来对待了;
第4行有标记成了长按下的行为;PRESSED也是一个final型的变量 它的存在的意义就是为了判断是不是长按下,基本原理也是通过二进制的某一位是0还是1来的
第5行更新背景
第6行,(mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE判断是不是可长按,这个行为如果我们在代码里面设置长按监听 或者是可以长按的话 就是可以的,没有的
话,这个条件就不成立;postCheckForLongClick的源代码如下
private void postCheckForLongClick(int delayOffset) { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.rememberWindowAttachCount(); postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() - delayOffset); }这里其实最主要的就是 mPendingCheckForLongPress ,而ViewConfiguration.getLongPressTimeout()的值源码中是500毫秒,超过了500毫秒就会检查是不是长按了
class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; public void run() { if (isPressed() && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { if (performLongClick()) { mHasPerformedLongPress = true; } } } public void rememberWindowAttachCount() { mOriginalWindowAttachCount = mWindowAttachCount; } }从 06行开始,isPressed()源码如下:
public boolean isPressed() { return (mPrivateFlags & PRESSED) == PRESSED; }看了吗?这个条件此时一定是true
mParent!=null也一定是成立的 在任何View通过setContentView方法加入到Activity中的时候,一定有父View,
最后一个条件 mOriginalWindowAttachCount == mWindowAttachCount 看看上一个源码片段的 rememberWindowAttachCount方法就知道也一定是true
如果都成立那就执行performLongClick方法了
public boolean performLongClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); boolean handled = false; if (mOnLongClickListener != null) { handled = mOnLongClickListener.onLongClick(View.this); } if (!handled) { handled = showContextMenu(); } if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } return handled; }看看第6行代码 我们自己实现的长按点击事件中onLongClick方法如果返回true 最后这个方法就返回true,如果返回false,这个方法最后就返回false
对于返回true,通过看上面的源代码很好理解,如果返回的是false ,这里我们也要分析一下为什么最终这个方法也返回的是false
handled = showContextMenu();主要是这行代码
public boolean showContextMenu() { return getParent().showContextMenuForChild(this); }getParent()一定是一个ViewGruop吧?那么去到ViewGruop中去看看这个方法
public boolean showContextMenuForChild(View originalView) { return mParent != null && mParent.showContextMenuForChild(originalView); }翻遍源码ViewGroup的所有子类都没有Overdide这个方法,那么 mParent!=null最后一定会是false,因为顶级的DecoerView没有父View;是不是!
这里长安监听的方法返回treu还是false对后面有什么影响?这里先保留,继续往后看源代码吧
MotionEvent.ACTION_MOVE
移动的时候的源代码
case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = mTouchSlop; if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } } break;首先第6行mTouchSlop是系统所能识别的最小滑动距离,他是从哪里来的 在初始化的时候View获取的
public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; // Used for debug only //++sInstanceCount; mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); }
/** * @return Distance a touch can wander before we think the user is scrolling in pixels */ public int getScaledTouchSlop() { return mTouchSlop; }在我们认定用户是在滑动像素之后,一个触摸要移动的距离,就是被当做滑动的最小移动距离
再看那么if条件 意思是不是 触摸超过了View宽高的+系统所能识别的最小滑动距离,也就是被认为滑动到了View的外面去了,这个时候就会执行里面的取消单击,取消长按按下行为:
/** * Remove the tap detection timer. */ private void removeTapCallback() { if (mPendingCheckForTap != null) { mPrivateFlags &= ~PREPRESSED; removeCallbacks(mPendingCheckForTap); } }
上面是取消单击行为和任务,代码之后都有分析,不难看懂
private void removeLongPressCallback() { if (mPendingCheckForLongPress != null) { removeCallbacks(mPendingCheckForLongPress); } }
取消长按任务,这个之后后面跟着取消了长按的标记
MotionEvent.ACTION_CANCEL
取消的源代码
case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); removeTapCallback(); break;
首先,我们要知道什么什么会收到这个事件MotionEvent.ACTION_CANCEL,上一个动作被父容器拦截的时候,子View会收到这样一个事件,比如用户一直在Moving,当某一个移动事件被父View拦截的时候,这个子View就会收到这个取消的事件;
分析代码
第2行代码是取消长按按下的标记
第3行是刷新背景
第4行是取消单击任务和标记
这个很简单是吧;
MotionEvent.ACTION_UP
最后我们再来看一个抬起事件
case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; if ((mPrivateFlags & 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 (!mHasPerformedLongPress) { // 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) { mPrivateFlags |= PRESSED; refreshDrawableState(); postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break;
首先,如果用户滑动的时候事件没有被取消或者没有移动到View之外的区域,那么第3行的这个条件一定会成立;
7~9行代码 if条件的意思是 是否可获取焦点 是否触摸可以获取焦点 是否已经获取了焦点
对于View本什么什么来说,这三个条件都是不成立的,这点可以在View初始化的时候通过获取的attr来看到,View的子类中
对于EditText来说这三个条件会成立,手指点击EditText的离开的时候,取得了焦点第三个条件就会成立;
成立了的话,通过requstFouse返回值就是true,否则的话,这个地方focusTaken 会一直是false;
11行代码mHasPerformedLongPress的这个值只有长按监听里面的 onLongClick方法返回的是true,这个值才是true;否则的话这个值就是false;
true的话这个条件不执行,用户的onLongClick方法执行了且返回了false,那么后续还是会执行以下单击监听事件,~~~
16行 会进入这个if条件 除非是EditText,
20~24行代码是 要执行点击监听事件
private final class PerformClick implements Runnable { public void run() { performClick(); } }看23行如果这个任务呗投放到消息队列里面没有被执行,会返回值false,通过本地再执行一次,如果返回了true,那么24行代码就不会执行了;
29行以后的代码 主要就是刷新状态 和背景的内容,前面都有分析和解释,也是很容易看懂的;
好了,Veiw的事件传递的代码,就分析了一遍,希望对每一个看了这个源代码的人都有帮助;
- View的事件传递源码详细解析
- View的事件传递源码解析
- ViewGroup的触摸事件的传递源码详细解析
- View的事件源码解析
- 开源项目源码解析-View 事件传递
- View的事件分发机制源码解析
- android View的事件分发源码解析
- View的事件传递
- view的事件传递
- View的事件传递
- View事件的传递
- Android View事件传递机制-源码分析
- Android View事件传递机制-源码分析
- Android View事件传递与源码分析
- Android 开源项目源码解析 -->公共技术点之 View 事件传递(一)
- 从源码的角度解析View的事件分发
- 从源码的角度解析View的事件分发
- 从源码的角度解析View的事件分发
- [数据结构]深度优先搜索算法(Depth-First-Search,DFS)
- Linux上搭建 vsftp(CentOS)
- hadoop安装笔记
- 反腐剧《人民的名义》官商勾结大揭秘,腐败分子大抓捕
- 数学取模运算——1的个数
- View的事件传递源码详细解析
- 仿腾讯新闻网动态获取数据
- 建造者模式
- css3
- Linux创建空文件的方法
- LeetCode : Range Sum Query
- 银行系统基础查询
- React Native Touchable(按钮) onPress 事件系列总结
- 使用eclipse创建maven项目卡住