一、Android中与Touch相关的方法有:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)。
二、首先,我们来讲一下View的事件分发。
我们拿一个简单的例子:在一个Activity中有一个Button,然后
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("TAG","onClick");
}
});
btn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.d("TAG","onTouch action:" + motionEvent.getAction());
return false;
}
});
我们点击Button,打印的日志如下:
12-09 10:22:48.791 24920-24920/com.example.administrator.touchtest D/TAG: onTouch action:0
12-09 10:22:48.811 24920-24920/com.example.administrator.touchtest D/TAG: onTouch action:2
12-09 10:22:48.831 24920-24920/com.example.administrator.touchtest D/TAG: onTouch action:2
12-09 10:22:48.851 24920-24920/com.example.administrator.touchtest D/TAG: onTouch action:2
12-09 10:22:48.851 24920-24920/com.example.administrator.touchtest D/ViewRootImpl: ViewPostImeInputStage processPointer 1
12-09 10:22:48.851 24920-24920/com.example.administrator.touchtest D/TAG: onTouch action:1
12-09 10:22:48.861 24920-24920/com.example.administrator.touchtest D/TAG: onClick
可以看到onTouch是优先于onClick执行的。
那么如果我们让onTouch返回true呢?结果如下:
12-09 10:26:09.671 2761-2761/com.example.administrator.touchtest D/TAG: onTouch action:0
12-09 10:26:09.701 2761-2761/com.example.administrator.touchtest D/TAG: onTouch action:2
12-09 10:26:09.721 2761-2761/com.example.administrator.touchtest D/TAG: onTouch action:2
12-09 10:26:09.741 2761-2761/com.example.administrator.touchtest D/TAG: onTouch action:2
12-09 10:26:09.741 2761-2761/com.example.administrator.touchtest D/ViewRootImpl: ViewPostImeInputStage processPointer 1
12-09 10:26:09.741 2761-2761/com.example.administrator.touchtest D/TAG: onTouch action:1
可以发现onClick未执行。那么这是问什么呢?接下来我们带着这个问题去分析源码。
我们知道只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent(MotionEvent ev)
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
- mOnTouchListener.onTouch(this, event)) {
- return true;
- }
- return onTouchEvent(event);
- }
mOnTouchListener 是在
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
这里赋值的。
第二个条件恒定成立
因此关键是看第三个条件了:onTouch的返回值。如果为false,则会执行onTouchEvent方法,否则直接返回true。所以onClick是在onTouchEvent方法中执行的。
好了,上面那个问题已经解决了。我们再去看一下onTouchEvent的源码。
- 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;
- }
onClick的执行就在第38行performClick(); 中,它的源码如下:
- public boolean performClick() {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- if (mOnClickListener != null) {
- playSoundEffect(SoundEffectConstants.CLICK);
- mOnClickListener.onClick(this);
- return true;
- }
- return false;
- }
在下一篇文章中我们将分析ViewGroup中的事件分发。