事件分发机制

来源:互联网 发布:王心凌怎么消失了知乎 编辑:程序博客网 时间:2024/06/15 02:12
参考出处:http://blog.csdn.net/guolin_blog/article/details/9097463
 
1.View事件分发
demo code:
  1. btn =(Button) findViewById(R.id.send);btn.setOnClickListener(newOnClickListener(){    @Override     publicvoid onClick(View v){     Log.d("TAG","onClick execute");    }});btn.setOnTouchListener(newOnTouchListener(){    @Override    publicboolean onTouch(View v,MotionEvent event){    Log.d("TAG","onTouch execute, action "+ event.getAction());    returnfalse;    }});

任何控件都会调用到dispatchTouchEvent事件分发,本来没有这个方法就往父类找。

  1. publicboolean dispatchTouchEvent(MotionEvent event){if(mOnTouchListener !=null&&(mViewFlags & ENABLED_MASK)== ENABLED &&   mOnTouchListener.onTouch(this, event)){    return true;} return onTouchEvent(event);}
mOntouchListener赋值,只要给控件注册事件就赋值。
  1. publicvoid setOnTouchListener(OnTouchListener l){   mOnTouchListener = l;}
mViewFlags & ENABLED_MASK == ENABLED:按钮默认是enableaa。
mOnTouchListener.onTouch:回调控件注册事件的onTouch方法,如果控件的ouTouch返回false,就执行onTouchEvent(event)
onTouch返回true,则dispatchTouchEvent也返回true,则onClick就不会执行,因为onClick是在onTouchEvent中的。
onTouchEvent的源码中的performClick方法:
  1. publicboolean performClick(){sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);if(mOnClickListener !=null){   playSoundEffect(SoundEffectConstants.CLICK);   mOnClickListener.onClick(this);   returntrue;}   returnfalse;}
只要mOnclickListener不为空就会去执行onClick方法。
mOnclickListener赋值在哪里?
  1. publicvoid setOnClickListener(OnClickListener l){if(!isClickable()){   setClickable(true);   }   mOnClickListener = l;}
touch事件层级传递:
如果给一个控件注册了touch事件,每次触发的时候都会执行ACTION_DOWN/ACTION_MOVE/ACTION_UP事件。
事件分发时,只有前一个action返回true才会触发后一个action(即触发onTouchEvent)。
对于button和imageview这两类不同的控件:
button:本身可以点击,onTouch不管怎么设置,onTouchEvent都会返回true。
imageview:本身不可以点击,onTouch设置返回false,onTouchEvent也返回false,ACTION_DOWN执行后不会触发ACTION_UP
 
onTouch和onTouchEvent区别:
onTouch优先于onTouchEvent执行,onTouch中通过返回true将事件消费掉,onTouchEvent将不会执行。
onTouch执行需满足:mOntouchListener不能为空,点击事件必须为enable,如果控件是非enable的,
如果设置ouTouch永远得不到执行,如果监听Touch事件只能在控件中重写onTouchEvent方法。
 
2.ViewGroup事件分发
Demo code:
自定义viewGroup:MyLayout
  1. publicclassMyLayoutextendsLinearLayout{   publicMyLayout(Context context,AttributeSet attrs){   super(context, attrs);}   @Overridepublicboolean onInterceptTouchEvent(MotionEvent ev){   return true;   }}
MainActivity:
  1. myLayout =(LinearLayout) findViewById(R.id.my_layout);button1 =(Button) findViewById(R.id.button1);button2 =(Button) findViewById(R.id.button2);myLayout.setOnTouchListener(newOnTouchListener(){    @Override    publicboolean onTouch(View v,MotionEvent event){      Log.d("TAG","myLayout on touch");      return false;    }});button1.setOnClickListener(newOnClickListener(){    @Override     publicvoid onClick(View v){       Log.d("TAG","You clicked button1");     }});button2.setOnClickListener(newOnClickListener(){     @Override     publicvoid onClick(View v){     Log.d("TAG","You clicked button2");   }});
touch事件的传递是先传递到viewGroup再传递到view的。触摸了任何控件,就一定会调用该控件的dispatchTouchEvent方法。当你点击了某个控件
,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。
ViewGroup的dispatchTouchEvent源码中:
  1. if(disallowIntercept ||!onInterceptTouchEvent(ev)){ev.setAction(MotionEvent.ACTION_DOWN);finalint scrolledXInt =(int) scrolledXFloat;finalint scrolledYInt =(int) scrolledYFloat;finalView[] children = mChildren;finalint count = mChildrenCount;
disallowIntercept是是否禁用事件拦截功能,默认flase,requestDisallowInterceptTouchEvent方法可修改。
我们在自定义的ViewGroup的onInterceptTouchEvent返回true。那么这段代码就默认flase,则不执行。
接下来就会执行
  1. if(target ==null){ev.setLocation(xf, yf);if((mPrivateFlags & CANCEL_NEXT_UP_EVENT)!=0){    ev.setAction(MotionEvent.ACTION_CANCEL);    mPrivateFlags &=~CANCEL_NEXT_UP_EVENT;}  return super.dispatchTouchEvent(ev);}
会调用MyLayout父类即View的方法,MyLayout注册的onTouchEvent会执行。
否则:
  1. child.mPrivateFlags &=~CANCEL_NEXT_UP_EVENT;   if(child.dispatchTouchEvent(ev)){    mMotionTarget = child;    returntrue;}
进入到子view的代码中,返回true,则MyLayout的onTouch都不会执行。
onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,
返回false代表不对事件进行拦截,默认返回false。
 
 
总结几个方法:
1、当用户触摸屏幕时,activity会接受这个事件,然后分发给下一级(view容器),view容器再分发给自己的onInterceptTouchEvent,如果返回结果是false(也就是不拦截),事件就会再传递给下一级(view控件),当view控件发现下面没得传了,也就是已经是最底层的view,此时该view就会消化该事件,即将事件传递给自己的onTouchEvent处理,若这时onTouchEvent处理不了该事件(返回false),这时事件传递到上一级的onTouchEvent,若还处理不了,继续向上传递;
 
2、当事件被onInterceptTouchEvent拦截时,事件就不会往下传递了,先把事件传递给自己的onTouchEvent消化,若消化不了,还是同样的规则往上传;
 
3、dispatchTouchEvent()方法中有记忆的功能,事件往下传递的时候,他会记录传递是否成功,这是根据当事件往上传时,就说明下面的view传递不成功或处理不成功;当第二次事件向下传递到处理不了的View,该View的dispatchTouchEvent()方法会判断,就不会往下传递了,直接把事件交给自己的onTouchEvent()方法来处理;如果上次的事件由下面的view成功处理了,那么这次事件继续往下传递; 
 
值得注意的是:dispatchTouchEvent的记忆功能是一系列的事件完成的时候才有效,过了就无效了,例如,触碰屏幕ACTION_DOWN , 然后滑动ACTION_MOVE , 最后抬起手ACTION_UP,这三个动作为一系列的事件,当你ACTION_DOWN时,dispatchTouchEvent就会记录你传递事件的“痕迹”,接着按着这个记忆来传递你的ACTION_MOVE 和 ACTION_UP,注意要一系列完成的动作才构成这一记忆事件,事件过后,dispatchTouchEvent自动消除记忆。
 
 
0 0
原创粉丝点击