- dispatchTouchEvent
- 代码不长,重要的地方有两个,分别是调用mOnTouchListener.onTouch(this, event)和onTouchEvent(event)
- 12行默认返回false
- 29到34行:如果mOnTouchListener不为空,执行mOnTouchListener.onTouch(this, event)方法,这个方法就是public void setOnTouchListener(OnTouchListener l) { getListenerInfo().mOnTouchListener = l;},如果onTouch(this, event)方法返回true,result=true
- 36到38,如果上面onTouch方法返回true,不会执行下面的onTouchEvent(event)方法,接下来看onTouchEvent方法
- onTouchEvent
- 这个代码稍微长一点,还是抓重点分析
- 代码中两次出现了下面的判断
(((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
第一次:7到16行,状态为DISABLE即代码中调用view.setEnable(true)时,返回上面的结果
第二次:23行到133行,即函数结束前的一个大括号,满足上面的条件返回true这里说明只要View满足clickable、longClickable和contextClickable(api23开始支持)的任何一项,onTouchEvent返回true,返回true意味着可以消费onTouchEvent事件,使得事件不再继续向上传递。
举个例子:下面的代码onTouchEvent事件的
case MotionEvent.ACTION_MOVE:不会执行,后面会说明原因。
public class MyView extends TextView {
protected static final String TAG = "MyButton";
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "MyView onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "MyView onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "MyView onTouchEvent ACTION_UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
} - 26到131行对event三种动作的解析,核心代码出来了
- 先说ACTION_DOWN,Touch的第一个动作计划司ACOIN_DOWN,80到105行.这里面设计三个动作分别是onclick,longclick和CheckForTap
- 88行,判断view是否在可以滑动的组件里面,例如:scrollview和listview
public boolean isInScrollingContainer() {
ViewParent p = getParent();
while (p != null && p instanceof ViewGroup) {
if (((ViewGroup) p).shouldDelayChildPressedState()) {
return true;
}
p = p.getParent();
}
return false;
} - 94到99行,如果发生touch事件的view在可以滑动的组件里面,执行postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());这个方法的意思是延迟 TAP_TIMEOUT(100毫秒)后,执行CheckForTap()方法,如下:
private final class CheckForTap implements Runnable {
public float x;
public float y;
@Override
public void run() {
mPrivateFlags &= ~PFLAG_PREPRESSED;
setPressed(true, x, y);
checkForLongClick(ViewConfiguration.getTapTimeout(), x, y);
}
}
三行代码:- 设置状态mPrivateFlags为false,这个后面后用到
- 设置press状态
- checkForLongClick,longClick事件,注意这里的参数传的是ViewConfiguration.getTapTimeout(), 即检查滑动延迟的TAP_TIMEOUT(100毫秒) 这个函数下面详细说明
- 继续向下103行checkForLongClick(0, x, y)有调用了这个函数
- 很简单就是调用mOnLongClickListener.onLongClick方法,注意下面的代码做了对LONG_CLICKABLE的判断,也即是说如果设置了view.setOnLongClickListener(false)则无法执行onLongClick事件
private void checkForLongClick(int delayOffset, float x, float y) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.setAnchor(x, y);
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
}
} - onLongClick触发的条件是执行完ACTION_DOWN动作后500毫秒,这里的两个动作都采用了postDelay的方式执行,这里是为了方便ACTION_UP或ACTION_MOVE动作发生时,可以直接将未执行的延迟任务remove掉
- 很简单就是调用mOnLongClickListener.onLongClick方法,注意下面的代码做了对LONG_CLICKABLE的判断,也即是说如果设置了view.setOnLongClickListener(false)则无法执行onLongClick事件
- ACTION_DOWN动作说完了,总结一下就是:
- 如果执行ACTION_DOWN的view在可以滑动的组件内,犹豫不能确定是不是滑动作,给了一个100毫秒的延迟动作
- 延迟500毫秒执行longClick事件
- 88行,判断view是否在可以滑动的组件里面,例如:scrollview和listview
- 27到78行下面看最复杂的ACTION_UP事件
- 注意看33行的focusTaken判断,再看50行,连起来就是如果view可以获得焦点并且已经获得了焦点则不能再执行click事件(例如edittext不能执行onClick)
- 51到53说明了即使是立即执行的onClick方法也推荐使用post,目的就是使view点击后视图状态先发生变化,真是煞费苦心啊
- 45到47行,removeLongPressCallback();此时如果postDelay500ms的longClock事件没有发生,将其remove掉
- 剩下的两个时间ACTION_CANCEL和ACTION_MOVE两个事件比较简单,做了一些动作处理,设置Press状态,移除longClick和TapCallBack动作
- 至此onTouchEvent解析完毕,有点罗嗦,总结一下
- 代码中setEnable和setClickable的作用相信很多人都曾困扰过,setEnable为false不能点击,setLongClickable为false不能长按,setClickable为false对onClick事件没有直接影响,就是名字唬人而已
- View的onClickable、onLongClickable,onContextClickable任一成立,该View会消费此onTouchEvent事件,这个非常重要
- longClick事件在TOUCH_DOWN中连续摁下500ms执行,onClick在TOUCH_UP中执行,两者可以同时执行,longClick返回false时。处于焦点状态的View不能执行onClick事件
- 未完待续
安卓事件分发框架
最新推荐文章于 2021-03-02 14:32:44 发布