Android 事件分发机制详解

本文详细解析了Android中触摸事件的分发流程,从Activity的dispatchTouchEvent开始,经过ViewGroup和View的dispatchTouchEvent,直至事件被某个View消费。重点介绍了事件拦截、传递和消费的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Activity dispatchTouchEvent

1. Activity的dispatchTochEvent作为事件入口

2. 如果事件为ACTION_DOWN(表示一整个触摸事件的开始),则调用onUserInteraction()(空方法),可重写,表示用户的一次交互

3. 通过 getWindow().superDispatchTouchEvent(ev) -> mDecor.superDispatchTouchEvent(event) -> mDecor.dispatchTouchEvent(event) 调用链
将event传递给Activity根View对象(decorView)

4. 如果该事件没被消费,将会调用onTouchEvent方法处理,并返回处理结果:点击事件在Window边界外返回true,一般情况都返回false

相关代码.

//源码分析:Activity.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
	// 一般事件列开始都是DOWN事件 = 按下事件,故此处基本是true
	if (ev.getAction() == MotionEvent.ACTION_DOWN) {
		//	a. 该方法为空方法
		//	b. 当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
		onUserInteraction();
	}
	//	a. getWindow() = 获取Window类的对象
	//	b. Window类是抽象类,其唯一实现类 = PhoneWindow类
	//		即此处的Window类对象 = PhoneWindow类对象
	//	c. Window类的superDispatchTouchEvent() = 1个抽象方法,由子类PhoneWindow类实现
	//	d. 该方法调用 mDecor.superDispatchTouchEvent(event) 
	//		-> mDecor.dispatchTouchEvent(event)
	//	e. mDecor = 顶层View (DecorView) 的实例对象
	if (getWindow().superDispatchTouchEvent(ev)) {
		return true;
		// 若getWindow().superDispatchTouchEvent(ev)的返回true
		// 则Activity.dispatchTouchEvent()就返回true,则方法结束。
		// 即 :该点击事件停止往下传递 & 事件传递过程结束
		// 否则:继续往下调用Activity.onTouchEvent
	}
	// 当事件未被任何view消费时
	// 该方法只有在点击事件在Window边界外才会返回true,一般情况都返回false
	return onTouchEvent(ev);
}

ViewGroup dispatchTouchEvent

相关代码

//源码分析:ViewGroup.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) { 
    
	...// 仅贴出关键代码

	// ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
	// 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false)
	// 判断值2: !onInterceptTouchEvent(ev) 触发拦截,并返回是否拦截成功
	if (disallowIntercept || !onInterceptTouchEvent(ev)) {
		// 拦截失败或不允许拦截都会进入该代码块
		ev.setAction(MotionEvent.ACTION_DOWN);
		final int scrolledXInt = (int) scrolledXFloat;
		final int scrolledYInt = (int) scrolledYFloat;
		final View[] children = mChildren;
		final int count = mChildrenCount;
		// 遍历了当前ViewGroup下的所有子View
		for (int i = count - 1; i >= 0; i--) {
			final View child = children[i];
			if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
					|| child.getAnimation() != null) {
				child.getHitRect(frame);
	
				// 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
				if (frame.contains(scrolledXInt, scrolledYInt)) {
					final float xc = scrolledXFloat - child.mLeft;
					final float yc = scrolledYFloat - child.mTop;
					ev.setLocation(xc, yc);
					child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
	
					// 条件判断的内部调用了该View的dispatchTouchEvent()
					// 实现了点击事件从ViewGroup到子View的传递
					if (child.dispatchTouchEvent(ev)) {
	
						mMotionTarget = child;
						// 事件被消费,直接返回,结束该事件传递
						return true;
					}
				}
			}
		}
	}
	
	// 拦截成功 ||
	// 子View没有消费该事件 || 点击的时空白处(没有任何View接收事件) -> 执行以下逻辑

	boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
          		(action == MotionEvent.ACTION_CANCEL);
	if (isUpOrCancel) {
  		mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
	}
	final View target = mMotionTarget;
        
	if (target == null) {
		ev.setLocation(xf, yf);
		if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
			ev.setAction(MotionEvent.ACTION_CANCEL);
			mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
		}
		
		// 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
		// 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick()
		// 即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())
		// 此处需与上面区别:子View的dispatchTouchEvent()
		return super.dispatchTouchEvent(ev);
	}
        
	...

}

View dispatchTouchEvent

相关代码

//源码分析:View.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent event) {
	// 设置了TouchListener && 当前View可用 &&
	// TouchListener.onTouch()返回 true
	if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
			mOnTouchListener.onTouch(this, event)) {
		//返回 true 消费该事件  
		return true;
	}
	// 该方法 ->> View 可点击返回 true 其他返回 false
	// 处理View的触摸事件,会调用
	// performClickInternal() -> performClick() ->
	// OnClickListener.onClick() 
	return onTouchEvent(event);  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值