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

onTouchEvent

最终会走到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;
}
}

TestViewAction

分别点击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层的分析可以参考如下文章

demanmath.com

关注公共号:

参考:

《深入理解android设计思想》   林学森

最新文章

  1. javaweb优化
  2. [wxWidgets] 1. 安装及&quot;hello world&quot;程序
  3. [C#反射]C#中的反射解析及使用.
  4. POJ 1061 青蛙的约会【扩展欧几里德】
  5. 【Framework】深入研究Asp.net页面的生命周期
  6. python网络编程——将IPv4地址转换成不同的格式
  7. 请注意CSDN社区微通道,许多其他的精彩等着你
  8. 【优先队列】-HDU4546比赛难度
  9. java项目和java-web项目中文件和文件夹的含义
  10. cmd 创建用户,并授权管理员权限就可以远程登陆了
  11. port bridge enable命令导致的环路
  12. ZT Linux可用的最新版本的sublime text注册
  13. django-debug-toolbar 安装及配置 django性能监控及调试
  14. MyEclipse Web项目部署失败:Deployment failure on Tomcat 7.x.Could not copy all resources to XXX.
  15. [UVA227][ACM/ICPC WF 1993]Puzzle (恶心模拟)
  16. Flutter - 本地化语言
  17. This version of the rendering library is more recent than your version of ADT plug-in. Please update ADT plug-in
  18. 利用WebApplicationInitializer配置SpringMVC取代web.xml
  19. Xshell拖拽上传文件插件
  20. 如何解决前后端token过期问题

热门文章

  1. canvas学习笔记
  2. StructureMap.dll 中的 GetInstance 重载 + 如何利用 反射动态创建泛型类
  3. [emacs] 使用ggtags浏览代码
  4. 【原创】SQL审核系统
  5. 做一个会使用PS的前端开发
  6. 谈谈asp.net MVC中的AppendTrailingSlash以及LowercaseUrls ,你还记得吗?
  7. C#测试题若干,都是基础阿
  8. Z.ExtensionMethods 一个强大的开源扩展库
  9. [转]DbFirst数据验证
  10. JAVA使用JDBC技术操作SqlServer数据库执行存储过程