【安卓笔记】touch事件的分发和消费机制
package com.example.toucheventdemo; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; public class MainActivity extends Activity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"dispatchTouchEvent--ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"onTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"onTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"onTouchEvent--ACTION_UP"); break; } return super.onTouchEvent(event); } }日志信息:
package com.example.toucheventdemo; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.Button; public class MyButton extends Button { private static final String TAG = "MyButton"; public MyButton(Context context) { super(context); } public MyButton(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"dispatchTouchEvent--ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"onTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"onTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"onTouchEvent--ACTION_UP"); break; } return super.onTouchEvent(event); } }mainActivity代码如下:
package com.example.toucheventdemo; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; public class MainActivity extends Activity { private static final String TAG = "MainActivity"; private MyButton but = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); but = (MyButton) findViewById(R.id.but); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"dispatchTouchEvent--ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"onTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"onTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"onTouchEvent--ACTION_UP"); break; } return super.onTouchEvent(event); } }此时点击Button按钮,查看日志:
package com.example.toucheventdemo; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; public class MainActivity extends Activity implements OnClickListener,OnTouchListener { private static final String TAG = "MainActivity"; private MyButton but = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); but = (MyButton) findViewById(R.id.but); but.setOnClickListener(this); but.setOnTouchListener(this); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"dispatchTouchEvent--ACTION_UP"); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"onTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"onTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"onTouchEvent--ACTION_UP"); } return super.onTouchEvent(event); } @Override public void onClick(View v) { Log.i("MyButton","ONCLICK"); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i("MyButton","onTouch--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i("MyButton","onTouch--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i("MyButton","onTouch--ACTION_UP"); break; } return false; } }现在点击按钮日志打印如下信息:
case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0; if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // take focus if we don‘t have it already and we should in // touch mode. boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { focusTaken = requestFocus(); } if (prepressed) { // The button is being released before we actually // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. setPressed(true); } if (!mHasPerformedLongPress) { // 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(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break;在ACTION_UP分支上执行了click操作,具体由performClick方法执行:
public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); return true; } return false; }
package com.example.toucheventdemo; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; public class MyLinearLayout extends LinearLayout { private static final String TAG = "MyLinearLayout"; public MyLinearLayout(Context context) { super(context); } public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"onTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"onTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"onTouchEvent--ACTION_UP"); } return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"dispatchTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"dispatchTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"dispatchTouchEvent--ACTION_UP"); } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG,"onInterceptTouchEvent--ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG,"onInterceptTouchEvent--ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.i(TAG,"onInterceptTouchEvent--ACTION_UP"); } return super.onInterceptTouchEvent(ev); } }此时再点击按钮,查看日志:
- 隧道方式:从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递。
- 冒泡方式:从最内层子元素依次往外传递直到根元素或在中间某一元素中由于某一条件停止传递。
Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下:
- 如果 return true,事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费,同时事件会停止向下传递;
- 如果 return false,事件分发分为两种情况:
- 如果当前 View 获取的事件直接来自 Activity,则会将事件返回给 Activity 的 onTouchEvent 进行消费;
- 如果当前 View 获取的事件来自外层父控件,则会将事件返回给父 View 的 onTouchEvent 进行消费。
事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下:
- 如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;
- 如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发;
- 如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),事件默认会被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。
在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的情况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑如下:
- 如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回 false,这个事件就会“消失”,而且接收不到下一次事件。
- 如果返回了 true 则会接收并消费该事件。
- 如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。