android 事件分发机制
1.View的事件分发机制
一个button,简单一点就是onTouch,还有onclick事件,我们一个一个来分析
首先响应的是dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
其实,在android源码的命名还是很有规律的,dispatchXXX,也就是分发机制,往往就是第一个需要响应的地方。
我们来分析下:touchlistener不为空,也就是view的使用者设置了回调。
第二个条件就是View必须是enable的。第三:onTouch返回false,就说明onTouch不消费该事件,由OnTouchEvent响应。
如果返回True,那么就会直接return。
所以onClick事件一定会被调到。
public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { // 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)); } if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; } } if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; if ((mPrivateFlags & 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 (!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) { mPrivateFlags |= PRESSED; refreshDrawableState(); postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: if (mPendingCheckForTap == null) { mPendingCheckForTap = new CheckForTap(); } mPrivateFlags |= PREPRESSED; mHasPerformedLongPress = false; postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break; case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); removeTapCallback(); break; case MotionEvent.ACTION_MOVE: final int x = (int) event.getX(); final int y = (int) event.getY(); // Be lenient about moving outside of buttons int slop = mTouchSlop; if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button removeTapCallback(); if ((mPrivateFlags & PRESSED) != 0) { // Remove any future long press/tap checks removeLongPressCallback(); // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } } break; } return true; } return false; }
最终会走到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; }
可以看到,如果setOnClickListener, onClick 就会走到。
2.ViewGroup的事件分发机制
<com.joyfulmath.frameworksample.viewdemo.MyLayout android:id="@+id/my_layout" android:background="#99000044" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/button_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button"/> <Button android:id="@+id/imageId" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/ic_lock_power_off"/> </com.joyfulmath.frameworksample.viewdemo.MyLayout>
一个layout里面有2个button,
package com.joyfulmath.frameworksample.viewdemo; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import com.joyfulmath.frameworksample.R; /** * Created by Administrator on 2016/8/27 0027. */ public class TestViewAction extends Activity implements View.OnClickListener,View.OnTouchListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.content_main); Button button = (Button) findViewById(R.id.button_id); button.setOnClickListener(this); button.setOnTouchListener(this); Button imageView = (Button) findViewById(R.id.imageId); imageView.setOnClickListener(this); imageView.setOnTouchListener(this); MyLayout myLayout = (MyLayout) findViewById(R.id.my_layout); myLayout.setOnTouchListener(this); myLayout.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button_id: TraceLog.i("button_id"); break; case R.id.imageId: TraceLog.i("imageId"); break; case R.id.my_layout: TraceLog.i("my_layout"); break; } } @Override public boolean onTouch(View v, MotionEvent event) { switch (v.getId()) { case R.id.button_id: TraceLog.i("button_id"); break; case R.id.imageId: TraceLog.i("imageId"); break; case R.id.my_layout: TraceLog.i("my_layout"); break; } return false; } }
分别点击button1 & button2 & 灰色部分
等到log如下:
08-27 10:19:26.799 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)] 08-27 10:19:26.880 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)] 08-27 10:19:26.896 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)] 08-27 10:19:26.913 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)] 08-27 10:19:26.926 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)] 08-27 10:19:26.926 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: button_id [at (TestViewAction.java:38)] 08-27 10:19:27.434 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)] 08-27 10:19:27.535 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)] 08-27 10:19:27.543 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)] 08-27 10:19:27.544 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: imageId [at (TestViewAction.java:41)] 08-27 10:19:28.111 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)] 08-27 10:19:28.156 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)] 08-27 10:19:28.173 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)] 08-27 10:19:28.190 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)] 08-27 10:19:28.237 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)] 08-27 10:19:28.237 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: my_layout [at (TestViewAction.java:44)]
也就是点击button1以后,不会传递都layout
But,如果layout里面有一个函数
public boolean onInterceptTouchEvent(MotionEvent ev)
这个函数就是截断对button的分发处理,默认是return false。
至此,我们有了一个大概的流程。
Activtiy->ViewGroup->View
如果仔细分析就会发现,在Activity里面有一个getDocView。所以Activity里面有个RootView的概念。
言归正传,ViewGroup本质上也是一个View,所以,可以把模型简单的定性为Activtiy->ViewGroup->View 三层。
首先Activity里面有2个函数,我们分析看看:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { TraceLog.i(); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { TraceLog.i(); return super.onTouchEvent(event); }
所以大体流程如下:
1.@Activty.diapatchTouchEvent ->@Layout.dispatchTouchEvent->@layout.onInterceptTouchEvent return true/false
2.return true->@layout.onTouchEvent 后面部分同view
3.return false->@view.dispatchTouchEvent View的分发见上一片流程。
上面是2016年的文章分析,最新的java层的分析可以参考如下文章
关注公共号:
参考:
《深入理解android设计思想》 林学森
posted on 2016-08-28 08:31 Joyfulmath 阅读(18181) 评论(0) 编辑 收藏 举报