同样是在上一篇文章中代码,我们给Acitvity中的mView添加一个监听器:
myView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.v(MyLinearLayout.Tag, "OnClickListener:onClick");
Toast.makeText(MainActivity.this, "onClidk", 1).show();
}
});
当我们在单击mView时,发现没有Toast,通过测试代码发现:
OnClickListener:onClick在测试中也没有发现!
在这里我们就要思考了,明明为mView设置了监听器,为什么没有起到监听的作用呢???
然后再让大家看一个程序:现在把我们在MyView中重写 onTouchEvent注释掉,然后再运行程序
结果:
好了,现在我们发现OnClickListener:onClick出现了,而且Toast在界面上也出现了,这时候你想到了什么?
但此时,我们又发现了问题,为什么MainActicity:dispatchTouchEvent…OnTouchListener:onTouch;它们却出现了两次,而OnClickListener:onClick却出现了一次;
我们再从头分析一下,首先我们重写了MyView中的onTouchEvent(),我们发现 OnClickListener:onClick 不见了(也就是说没被调用);我们去掉重写的onTouchEvent(),此时OnClickListener:onClick 出现了(也就是说被调用啦);但又有个问题出现了,其他的都出现了两次,而OnClickListener:onClick 出现了一次。
现在让我们来分析为什么会OnClickListener:onClick出现一次,其他出现两次;
通过代码分析感觉有点小麻烦(还要附加代码),我们可以通过视觉的效果来观察出这个问题的出现。首先我们先通过手指触摸MyView(不要松手),通过观察LogCat我们可以发现:
然后把手松开,观察LogCat我们可以发现:
哈哈,你想到了什么?对,答案就如你所想。
首先我们先通过手指触摸MyView(不要松手),这是一个OnTouchEvent的ACTION_DOWN事件;手松开后,这是一个OnTouchEvent的ACTION_UP事件(在这里出现了OnClickListener:onClick)。
这两个问题联系到一起,你想到了什么? 对!答案正如你所想!(大胆去想,之后我们可以去验证);这两个问题联系到一起:在View.onTouchEvent()的ACTION_DOWN中消费了该事件;在ACTION_UP中我们调用了view.setOnclickListener()事件。
下面通过源码来分析一下onTouchEvent(event)函数到底做了什么吧。
/**
* 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) // 1、判断该view是否enable
// 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)) // 2、是否是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)) // post
performClick(); // 3、点击事件处理
}
}
}
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 (mPendingCheckFor