欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android事件分发机制(下) View的事件处理

程序员文章站 2024-03-31 11:04:16
综述   在上篇文章android中的事件分发机制(上)——viewgroup的事件分发中,对viewgroup的事件分发进行了详细的分析。在文章的最后viewgroup...

综述

  在上篇文章android中的事件分发机制(上)——viewgroup的事件分发中,对viewgroup的事件分发进行了详细的分析。在文章的最后viewgroup的dispatchtouchevent方法调用dispatchtransformedtouchevent方法成功将事件传递给viewgroup的子view。并交由子view进行处理。那么现在就来分析一下子view接收到事件以后是如何处理的。

view的事件处理

  对于这里描述的view,它是viewgroup的父类,并不包含任何的子元素。这也就意味着view无法再次向下对事件进行分发操作,因此在view中并不存在onintercepttouchevent方法,也不会对事件做出拦截操作。它所做的事情就是对所接收的事件进行处理。下面就开看一下view如何对事件进行处理的。
  既然viewgroup将事件交由view的dispatchtouchevent方。那么首先在这里就来看一下dispatchtouchevent里面做了什么事情。

public boolean dispatchtouchevent(motionevent event) {

  ......

  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;
}

  在view的dispatchtouchevent方法中对事件处理的核心部分体现在上述代码中。onfiltertoucheventforsecurity方法表示当前接收事件的view是否处于被遮盖状态,view处于被遮盖状态表示当前view不位于顶部,该view被其它view所覆盖。如果当前view被遮盖,那么该view不会对事件进行处理。

public interface ontouchlistener {
  boolean ontouch(view v, motionevent event);
}

public void setontouchlistener(ontouchlistener l) {
  getlistenerinfo().montouchlistener = l;
}

listenerinfo getlistenerinfo() {
  if (mlistenerinfo != null) {
    return mlistenerinfo;
  }
  mlistenerinfo = new listenerinfo();
  return mlistenerinfo;
}

  在结合上述一段代码可以看到,通过setontouchlistener方法设置ontouchlistener以后,若是当前view处于可用状态,那么条件li != null && li.montouchlistener !=null && (mviewflags & enabled_mask) == enabled必然为true。这时候程序便会回调ontouchlistener中的ontouch方法,若是在ontouch方法中返回true,便不会在执行view的ontouchevent方法。从这里我们能够看到,一旦设置了ontouchlistener,那么ontouchlistener的优先级要高于ontouchevent。
  有一点需要注意,在程序中设置了ontouchlistener以后,对于ontouchlistener中的ontouch的返回值并不代表view中的dispatchtouchevent方法所返回的值。在ontouch方法返回true的时候,表示事件成功被当前view所消耗,这时候result被置为true并且不再执行ontouchevent,所以dispatchtouchevent也就返回true。可是一旦在ontouch方法中返回false。这时候便会调用ontouchevent方法,如果事件被ontouchevent成功处理,并返回true,result依然会被置为true,dispatchtouchevent自然而然的也就返回true。
  下面在进入view的ontouchevent方法一探究竟。对于ontouchevent方法里的内容比较多,在这里分段查看。

if ((viewflags & enabled_mask) == disabled) {
  if (action == motionevent.action_up && (mprivateflags & pflag_pressed) != 0) {
    setpressed(false);
  }
  // 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)
      || (viewflags & context_clickable) == context_clickable);
}

  在这里可以看出对于不可用的view,如果他们的一些点击事件可用的话,依然能够成功的消费事件,只是它不会为该事件做出响应。对于view的这些点击之间默认为不可用,但是对于不同的的view他们的默认值不一样。例如在imageview中点击事件依然为不可用,但是在button中点击事件为可用。当然如果手动为它们设置监听事件,那么这些监听事件都将会自动被设为可用状态。从如下源码中可以看出。

public void setonclicklistener(@nullable onclicklistener l) {
  if (!isclickable()) {
    setclickable(true);
  }
  getlistenerinfo().monclicklistener = l;
}

public void setonlongclicklistener(@nullable onlongclicklistener l) {
  if (!islongclickable()) {
    setlongclickable(true);
  }
  getlistenerinfo().monlongclicklistener = l;
}

public void setoncontextclicklistener(@nullable oncontextclicklistener l) {
  if (!iscontextclickable()) {
    setcontextclickable(true);
  }
  getlistenerinfo().moncontextclicklistener = l;
}

  下面接着看ontouchevent里面代码。

if (mtouchdelegate != null) {
  if (mtouchdelegate.ontouchevent(event)) {
    return true;
  }
}

  这里首先判断是否对事件设置了代理,如果对事件设置了代理,便会执行touchdelegate的ontouchevent方法。mtouchdelegate默认值为null,可以通过view的settouchdelegate方法来设置代理。对于touchdelegate在后续的文章中在进行详细分析,在这里就不在过多描述。
  最后看一下view是如何处理事件的,对于接收的事件整个处理过程比较复杂,在这里就从宏观上来整体看一下它的处理机制。

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;

    ......

  }

  return true;
}

  如果view的点击事件处于可用状态的话,便会对于这些事件进行处理,并且返回true。当一个事件序列完成以后调用performclick方法,下面看下这个performclick方法。

public boolean performclick() {
  final boolean result;
  final listenerinfo li = mlistenerinfo;
  if (li != null && li.monclicklistener != null) {
    playsoundeffect(soundeffectconstants.click);
    li.monclicklistener.onclick(this);
    result = true;
  } else {
    result = false;
  }

  sendaccessibilityevent(accessibilityevent.type_view_clicked);
  return result;
}


  从上面代码中可以看出,如果我们设置了onclicklistener,便会调用它的onclick方法。从这一路下来我们可以看出对于view的onclick事件,在最后才会被调用,可见onclick的优先级是最低的。

总结

  在这里对view的事件处理做一下总结。在viewgroup将事件分发到view以后。在view里面通过ontouchlistener的ontouch和view中的ontouchevent这两个方法对事件进行处理。对于ontouch方法是view提供给用户的,方便用户自己处理触摸事件,而ontouchevent是android系统自己实现的接口。若是用户设置了ontouchlistener,android系统会首先调用ontouchlistener的ontouch方法。若是在ontouch方法中返回true,就不在执行view的ontouchevent方法。只有在ontouch中返回了false才会执行ontouchevent。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。