android事件分发(二)

这篇博客主要讲一下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事件分发机制就介绍到这里,欢迎补充和纠正。

你可能感兴趣的:(android,onTouchEvent,事件分发)