这篇博客主要讲一下dispatchTouchEvent,onTouchEvent,setOnTouchListener和setOnClickListener。
好了,这是我们的自定义的一个Button:
public class MyButton extends Button { public MyButton(Context context) { this(context,null); } public MyButton(Context context, AttributeSet attrs) { this(context,attrs,0); } public MyButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent event) { Log.e("touch", "MyButton-----dispatchTouchEvent---begin"); boolean b = super.dispatchTouchEvent(event); Log.e("touch", "MyButton-----dispatchTouchEvent---" + b); return b; } @Override public boolean onTouchEvent(MotionEvent event) { Log.e("touch", "MyButton-----onTouchEvent---begin"); boolean b = super.onTouchEvent(event); Log.e("touch", "MyButton-----onTouchEvent---" + b); return b; } }
在activity中:
button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.e("touch","onTouch"); return false; } }); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("touch","setOnClickListener"); } });
好了,运行一下,点击一下MyButton,控制台输出:
03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: onTouch 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---begin 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---true 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---true 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: onTouch 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---begin 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---true 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---true 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: setOnClickListener
从输出的日志,我们看一下流程:
首先执行MyButton的dispatchTouchEvent方法,在dispatchTouchEvent方法里又执行了View.OnTouchListener对象的onTouch方法,onTouch方法返回false,然后又在dispatchTouchEvent方法里执行了onTouchEvent方法,onTouchEvent方法的返回值为true,然后dispatchTouchEvent方法跑完,返回true,因为dispatchTouchEvent的返回值为true,然后继续消费,同样的方式又来一遍,最后才执行了setOnClickListener。其实这就是一个手指按下,然后放开的流程。
当然这个流程中有很多疑问点:我通过看源码的方式再走一遍流程:(注意我用的compileSdkVersion为23)
首先执行MyButton的dispatchTouchEvent方法(这里我只保留了关键代码)
public boolean dispatchTouchEvent(MotionEvent event) { ... boolean result = false; if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } } ... return result; }
大家看一下第7行,首先对于(li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED),大家都可以把这些条件判断为true,关键是li.mOnTouchListener.onTouch(this, event),也就是:
button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.e("touch","onTouch"); return false; } });
原来这里我们默认设置成了false,故li.mOnTouchListener.onTouch(this, event)为false,if判断也就不成立,result = true;没有执行,然后继续进入了语句:
if (!result && onTouchEvent(event)) { result = true; }
!result为真,然后执行onTouchEvent(event),看一下onTouchEvent(event)源码:(已过滤)
public boolean onTouchEvent(MotionEvent event) { ... if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { case MotionEvent.ACTION_UP: ... if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // 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(); } } ... break; case MotionEvent.ACTION_DOWN: ... break; case MotionEvent.ACTION_CANCEL: ... break; case MotionEvent.ACTION_MOVE: drawableHotspotChanged(x, y); ... break; } return true; } return false; }
看一下if条件,乱七八糟,但是当我们的MyButton的clickable为true是,这个if判断就会成立,然后进入判断里面的语句,最后return true,也就是onTouchEvent(event)的返回值为true,再看dispatchTouchEvent方法里的语句:
if (!result && onTouchEvent(event)) { result = true; }
也就成立,最后dispatchTouchEvent执行完,返回true,表示继续消费(也就是手指抬起的时候),当又一次执行到onTouchEvent(event)时,进入:
case MotionEvent.ACTION_UP:
然后发现了performClick();方法,performClick方法里又有li.mOnClickListener.onClick(this)语句,故setOnClickListener执行。
可能过程比较的复杂,但执行的流程概括起来就是:
03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: onTouch 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---begin 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---true 03-15 13:02:53.380 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---true 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: onTouch 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---begin 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---true 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---true 03-15 13:02:53.400 12324-12324/com.sumu.gefdemo E/touch: setOnClickListener
好了,现在我们把onTouch的返回值设置为true:
button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.e("touch","onTouch"); return true; } });
再执行一遍:
03-15 13:45:27.990 25048-25048/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:45:27.990 25048-25048/com.sumu.gefdemo E/touch: onTouch 03-15 13:45:27.990 25048-25048/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---true 03-15 13:45:28.040 25048-25048/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:45:28.040 25048-25048/com.sumu.gefdemo E/touch: onTouch 03-15 13:45:28.040 25048-25048/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---true
这次发现没有执行onTouchEvent方法,那也就没有执行setOnClickListener,原因就是dispatchTouchEvent中的语句:
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; }
成立了,result为true了,然后
if (!result && onTouchEvent(event)) { result = true; }
也就没有执行。
那我们把onTouchEvent的返回值设置为false,又回怎么样呢:
@Override public boolean onTouchEvent(MotionEvent event) { Log.e("touch", "MyButton-----onTouchEvent---begin"); boolean b = super.onTouchEvent(event); Log.e("touch", "MyButton-----onTouchEvent---" + b); return false; }
然后执行:
03-15 13:50:58.250 28642-28642/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---begin 03-15 13:50:58.250 28642-28642/com.sumu.gefdemo E/touch: onTouch 03-15 13:50:58.250 28642-28642/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---begin 03-15 13:50:58.250 28642-28642/com.sumu.gefdemo E/touch: MyButton-----onTouchEvent---true 03-15 13:50:58.250 28642-28642/com.sumu.gefdemo E/touch: MyButton-----dispatchTouchEvent---false
发现流程只走了一半,也就是只有手按下的流程,没有手抬起的流程,这是因为:
if (!result && onTouchEvent(event)) { result = true; }
中的result = true语句没有执行,导致dispatchTouchEvent方法返回false,也就不继续消费了。
好了,android事件分发机制就介绍到这里,欢迎补充和纠正。