在Android中,onClick、onLongClick的触发是和ACTION_DOWN及ACTION_UP相关的,在时序上,如果我们在一个View中同时覆写了onClick、onLongClick及onTouchEvent的话,onTouchEvent是最先捕捉到ACTION_DOWN和ACTION_UP事件的,其次才可能触发onClick或者onLongClick。主要的逻辑在View.java中的onTouchEvent方法中实现的:
<span style="font-size:14px;"><span style="color:#333333;">case MotionEvent.ACTION_DOWN:
mPrivateFlags |= PRESSED;
refreshDrawableState();
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
</span><span style="color:#ff0000;"> postCheckForLongClick(); </span><span style="color:#333333;">
}
break;</span></span>
<span style="font-size:14px;"><span style="color: rgb(51, 51, 51);">
case MotionEvent.ACTION_UP:
if ((mPrivateFlags & PRESSED) != 0) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
if (mPendingCheckForLongPress != null) {
removeCallbacks(mPendingCheckForLongPress);
}
if (!focusTaken) {
</span><span style="color:#ff0000;"> performClick();</span><span style="color:#333333;">
}
}
…
break;</span></span>
可以看到,
Click
的触发是在系统捕捉到
ACTION_UP
后发生并由
performClick()
执行的,
performClick
里会调用先前注册的监听器的
onClick()
方法:
<span style="font-size:14px;">public boolean performClick() {
…
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
<span style="color:#3366ff;">mOnClickListener.onClick(this);</span>
return true;
}</span>
<span style="font-size:14px;"> return false;
}</span>
ongClick 的触发则是从 ACTION_DOWN 开始,由 postCheckForLongClick() 方法完成:
<span style="color:#333333;">private void postCheckForLongClick() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
</span><span style="color:#3366ff;"> postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());</span><span style="color:#333333;">
}</span>
可以看到,在 ACTION_DOWN 事件被捕捉后,系统会开始触发一个 postDelayed 操作, delay 的时间在 Eclair2.1 上为 500ms , 500ms 后会触发 CheckForLongPress 线程的执行:
<span style="font-size:14px;"><span style="color:#333333;">class CheckForLongPress implements Runnable {
…
public void run() {
if (isPressed() && (mParent != null)
&& mOriginalWindowAttachCount == mWindowAttachCount) {
if (</span><span style="color:#3366ff;">performLongClick()</span><span style="color:#333333;">) {
mHasPerformedLongPress = true;
}
}
}
…
}</span></span>
如果各种条件都满足,那么在
CheckForLongPress
中执行
performLongClick()
,在这个方法中将调用
onLongClick()
:
<span style="color:#333333;">public boolean performLongClick() {
…
if (mOnLongClickListener != null) {
</span><span style="color:#3366ff;">handled = mOnLongClickListener.onLongClick(View.this);</span><span style="color:#333333;">
}
…
}</span>
Button的onTouch,onClick,onLongClick事件发生先后顺序和关联:
注意: onTouch事件中:down事件返回值标记此次事件是否为点击事件,返回false说明是点击事件,返回true不记为点击事件;up事件标记此次事件结束时间,也就是判断是否为长按。
一. onTouch返回false,执行顺序
1.如果长按:down-->onLongClick-->up-->onClick
2.如果点击:down-->up-->onClick
二. onTouch返回true,执行顺序:down-->up,不会触发onclick以及onLongClick
三. onTouch:down返回true,up返回false执行顺序结果同二:down-->up,不会触发onclick以及onLongClick机制分析: onTouch事件中:down返回true,系统将不把本次事件记录为点击事件,也就不会触发onClick或者onLongClick事件了。因此尽管当up的时候返回false,系统也不会继续触发onClick事件了。
四.onTouch:down返回false,up返回true执行顺序:
1.如果长按:down-->onLongClick-->up
2.如果点击:down-->up------>(达到一定时间后自动触发onLongClick)
机制分析: onTouch事件中:down返回false,说明此次事件为点击事件,而up返回了true,说明此次事件一直没有结束,也就是一直长按下去了,达到长按临界时间后,自然触发长按事件,而onClick事件没有触发到
结论:1.ontouch的down事件返回true一定不会触发onclick事件
2.ontouch的down事件返回false,up事件返回ture一定会触发onLongClick事件,并且不会触发onclick事件(因为up返回true长按事件会一直自动执行下去)