本文最后更新于:星期四, 六月 18日 2020, 9:01 上午


首先推荐郭霖的真正的通俗易懂的View的事件分发文章:

Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

文章中讲述了几个要点:

  • 如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。即消费事件才会继续有事件。
  • 在dispatchMotionEvent方法中执行到了onTouchEvent中将MotionEvent对象传入switch中做ACTION_DOWN、ACTION_UP等判断的时候,默认地在结束switch之后马上返回true。也就是到了这一步,默认地就消费了事件。
  • 如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。(默认地每一个控件的enable=true),并且设置OnClickListener也得不到执行。

我自己的一点学习笔记:

在View的dispatchTouchEvent(MotionEvent event)方法中,onTouch和onTouchEvent的优先级关系:mOnTouchListener.onTouch>onTouchEvent

onTouch:

由OnTouchListener接口控制,从外部setOnTouchListener来实现,返回true的话,就把事件拦截,则不会再执行下面的onTouchEvent。

button.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        eventLog(event);
        return false;
    }
});

onTouchEvent:

伪码如下

public boolean onTouchEvent(MotionEvent event) {

    boolean clickable =CLICKABLE;
    //如果CLICKABLE为true,才接着判断MotionEvent。
    if(clickable){
        switch(action){
            case MotionEvent.ACTION_UP:
                ...
                ...
                ...
                performClick();
                break;
            case  MotionEvent.ACTION_DOWN:
                break;
            case ..
            ...
        }
    }
}

伪码的意思就是:

  • 如果View的CLICKABLE属性是false,那么onTouchEvent就不能根据传入的MotionEvent对象才进行操作。
  • 注意ACTION_UP的最后面有一个performClick()方法,点进去==>
    public boolean performClick(){
      ...
      //这里的mOnClickListener就是===>
      //我们通过button.setOnClickListener(new 匿名内部类)传入的对象;
      if(mOnClickListener!=null){
          mOnClickListener.onClick(this);
      }
      ...
    }
    那么我们的onClick方法不会执行的原因可能有一种:
  • 我们设置OnTouchListener.onTouch方法返回了true。
  • View的CLICKABLE是false,这是不可能的,因为一旦setOnClickListener(),就会把CLICKABLE改为true==>>

此外,View的CLICKABLE默认就是false的,但是为什么button就算不设置OnClickListener也能被click呢?看一下Button的构造方法:

public class Button extends TextView {
    public Button(Context context) {
        this(context, null);
    }
    //看传入的第三个参数是R.attr.buttonStyle!!
    public Button(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.buttonStyle);
    }
    public Button(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }
    public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    //其他忽略
    ...

找到那个R.attr.buttonStyle关联的文件:
她在themes.xml文件中

<!-- Button styles -->
<item name="buttonStyle">@style/Widget.Button</item>
<item name="buttonStyleSmall">@style/Widget.Button.Small</item>
<item name="buttonStyleInset">@style/Widget.Button.Inset</item>
<item name="buttonStyleToggle">@style/Widget.Button.Toggle</item>

点进去buttonStyle==>

Button通过style已经设置了clickable=true。


View     

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

ScrollView嵌套ScrollView的滑动冲突 上一篇
Fragment状态保存 下一篇