目录

相关文章:

描述一下Android的事件分发机制?

两个实际遇到的案例:

1、 ScrollView和ListView滑动冲突:

Demo:

TouchEventActivity

 activity_touch_event

colors.xml

LocalRelativeLayout

 LocalButton

测试:

测试一:

测试二:

测试三:

测试四:

Android事件处理的三个重要函数

2.1 事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

2.2 事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

2.3 事件响应:public boolean onTouchEvent(MotionEvent ev)

事件分发流程图: 

为什么会有事件分发机制?

PhoneWindow和DecorView是什么?

三个重要的事件分发方法:

事件分发流程:


相关文章:

浅谈Android事件分发机制 (详细、专业)

完全理解android事件分发机制 (有demo)

Android:事件分发机制  (自己的)

Android事件分发之Activity篇 -- dispatchTouchEvent、onTouchEvent之间关系

描述一下Android的事件分发机制?

Android事件分发机制的本质:事件从哪个对象发出,经过哪些对象,最终由哪个对象处理了该事件。此处对象指的是Activity、Window与View。

Android事件的分发顺序:Activity(Window) -> ViewGroup -> View

Android事件的分发主要由三个方法来完成,如下所示:

// 父View调用dispatchTouchEvent()开始分发事件public boolean dispatchTouchEvent(MotionEvent event){    boolean consume = false;    // 父View决定是否拦截事件    if(onInterceptTouchEvent(event)){        // 父View调用onTouchEvent(event)消费事件,如果该方法返回true,表示        // 该View消费了该事件,后续该事件序列的事件(Down、Move、Up)将不会在传递        // 该其他View。        consume = onTouchEvent(event);    }else{        // 调用子View的dispatchTouchEvent(event)方法继续分发事件        consume = child.dispatchTouchEvent(event);    }    return consume;}

两个实际遇到的案例:

1、 ScrollView和ListView滑动冲突:

解决办法:重写ScrollView,在其onInterceptTouchEvent方法中进行相应处理

public class ListScrollView extends ScrollView {    private XListView xListView;    public ListScrollView(Context context) {        super(context);    }    public ListScrollView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public void setxListView(XListView xListView) {        this.xListView = xListView;    }    /**     * 覆写onInterceptTouchEvent方法,点击操作发生在ListView的区域的时候,     * 返回false让ScrollView的onTouchEvent接收不到MotionEvent,而是把Event传到下一级的控件中     */    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        if (xListView != null && checkArea(xListView, ev)) {            return false;        }        return super.onInterceptTouchEvent(ev);    }    /**     *  测试view是否在点击范围内     * @param v     * @return     */    private boolean checkArea(View v, MotionEvent event){        float x = event.getRawX();        float y = event.getRawY();        int[] locate = new int[2];        v.getLocationOnScreen(locate);        int l = locate[0];        int r = l + v.getWidth();        int t = locate[1];        int b = t + v.getHeight();        if (l < x && x < r && t < y && y < b) {            return true;        }        return false;    }}

Demo:

TouchEventActivity

public class TouchEventActivity extends AppCompatActivity {    @BindView(R.id.local_btn)    LocalButton localBtn;    @BindView(R.id.local_rl)    LocalRelativeLayout localRl;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_touch_event);        ButterKnife.bind(this);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        LogUtils.e("TouchEventActivity --> dispatchTouchEvent");        return super.dispatchTouchEvent(ev);//        return true;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        LogUtils.e("TouchEventActivity --> onTouchEvent");        return super.onTouchEvent(event);    }    @OnClick({R.id.local_btn, R.id.local_rl})    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.local_btn:                LogUtils.e("local_btn --> 我被点击了");                break;            case R.id.local_rl:                break;        }    }}

 activity_touch_event

<?xml version="1.0" encoding="utf-8"?>                

colors.xml

#ABDFDC    #87C09B    #B5BC85    #E7B1AD

LocalRelativeLayout

public class LocalRelativeLayout extends RelativeLayout {    public LocalRelativeLayout(Context context) {        this(context, null);    }    public LocalRelativeLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public LocalRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        LogUtils.e("LocalRelativeLayout --> dispatchTouchEvent");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        LogUtils.e("LocalRelativeLayout --> onInterceptTouchEvent");        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        LogUtils.e("LocalRelativeLayout --> onTouchEvent");        return super.onTouchEvent(event);    }}

 LocalButton

public class LocalButton extends android.support.v7.widget.AppCompatButton {    public LocalButton(Context context) {        this(context, null);    }    public LocalButton(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public LocalButton(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        LogUtils.e("LocalButton --> dispatchTouchEvent");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        LogUtils.e("LocalButton --> onTouchEvent");        return super.onTouchEvent(event);    }}

测试:

测试一:

Activity的dispatchTouchEvent()返回super.dispatchTouchEvent(ev),
LocalRelativeLayout的dispatchTouchEvent()返回super.dispatchTouchEvent(ev),结果如下:

 TouchEventActivity --> dispatchTouchEvent LocalRelativeLayout --> dispatchTouchEvent LocalRelativeLayout --> onInterceptTouchEvent LocalButton --> dispatchTouchEvent LocalButton --> onTouchEvent local_btn --> 我被点击了

说明: Android事件响应机制是“由外到内”分发、“由内到外”处理的形式实现的。

测试二:

将Activity的dispatchTouchEvent的返回值设为true或者false,那么事件都不会传到下一层。

  • return true : View消费所有事件。
  • return false :停止分发,交由上层控件的onTouchEvent方法进行消费,如果本层控件是Activity,那么事件将被系统消费、处理。

打印结果为:

TouchEventActivity --> dispatchTouchEvent

 结论:事件被Activity拦截了。

测试三:

Activity的dispatchTouchEvent()返回super.dispatchTouchEvent(ev),
LocalRelativeLayout的dispatchTouchEvent()返回false,

  • return false :停止分发,交由上层控件的onTouchEvent方法进行消费
 TouchEventActivity --> dispatchTouchEvent LocalRelativeLayout --> dispatchTouchEvent TouchEventActivity --> onTouchEvent

 结论:LocalRelativeLayout将事件返回给Activity的onTouchEvent处理。

测试四:

Activity的dispatchTouchEvent()返回super.dispatchTouchEvent(ev),
LocalRelativeLayout的dispatchTouchEvent()返回true,

 TouchEventActivity --> dispatchTouchEvent LocalRelativeLayout --> dispatchTouchEvent

 

Android事件处理的三个重要函数

Android事件分发机制主要由“事件分发”—>“事件拦截”—>“事件响应”这三步来进行逻辑控制的。本文也将从这三步对应的函数来分析。

2.1 事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

当监听到有触发事件时,首先由Activity进行捕获,然后事件就进入事件分发的流程。Activity本身没有事件拦截,从而将事件传递给最外层的View的dispatchTouchEvent(MotionEvent ev)方法,该方法将对事件进行分发。

  • return true : View消费所有事件。
  • return false :停止分发,交由上层控件的onTouchEvent方法进行消费,如果本层控件是Activity,那么事件将被系统消费、处理。
  • super.dispatchTouchEvent(ev): 将事件交由本层的事件拦截onInterceptTouchEvent方法处理。

2.2 事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

  • return true: 对事件拦截,交由本层的onTouchEvent进行处理。
  • return false: 不拦截,分发到子View,由子View的dispatchTouchEvent方法处理。
  • super.onInterceptTouchEvent(ev):默认表示事件拦截,交由本层的onTouchEvent进行处理。

2.3 事件响应:public boolean onTouchEvent(MotionEvent ev)

  • return true: 表示onTouchEvent处理完事件后消费了此次事件。
  • return false: 不响应事件,不断的传递给上层的onTouchEvent方法处理,直到某个View的onTouchEvent返回true,则认为该事件被消费。如果到最顶层View还是返回false,那么该事件不消费,将交由Activity的onTouchEvent进行处理。
  • return: super.onTouchEvent,不响应事件,结果与return返回false一样。
     

事件分发流程图: 

结合上面的理解,我们再来看看Touch事件传递机制流程图

 

为什么会有事件分发机制?

安卓上面的View是树形结构,View可能会重叠在一起,当我们点击的地方有多个View都可以响应的时候,这个点击事件应该由谁处理呢?为了解决这一个问题,就有了事件分发机制。我们看一下示例:

图一

再来看一下layout的布局结构:

图二​​

PhoneWindow和DecorView是什么?

在图二中我们看到了PhoneWindow和DecorView,这两个是什么玩意呢?

先来说一下DecorView,在图一中,没有被view覆盖的界面会显示主题的颜色,这部分区域以及最上面的标题也没有出现在layout布局当中,它们都属于DecorView。

PhoneWindow是抽象类Window的一个实现类,而Window是所有视图最顶层的管理容器。view视图、viewgroup的外观和行为还有背景显示、标题栏、事件处理都是属于Window来管理。但是它是抽象类,所以管理的实现都是由它唯一的实现PhoneWindow来实现。PhoneWindow也可以说是View的事件管理容器。而PhoneWindow一般都是通过它的内部类DecorView来进行消息传递的。PhoneWindow通过指示DecorView来给下面的View传递消息,而下面View的信息也是通过DecorView返回给PhoneWindow。

Window.java的源码:

public abstract class Window {        public interface Callback {        public boolean dispatchKeyEvent(KeyEvent event);        public boolean dispatchKeyShortcutEvent(KeyEvent event);        public boolean dispatchTouchEvent(MotionEvent event);        public boolean dispatchTrackballEvent(MotionEvent event);        public boolean dispatchGenericMotionEvent(MotionEvent event);        }}

PhoneWindow.java的源码:

public class PhoneWindow extends Window implements MenuBuilder.Callback {    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {        @Override        public boolean dispatchKeyEvent(KeyEvent event) {...}        @Override        public boolean dispatchKeyShortcutEvent(KeyEvent ev) {...}        @Override        public boolean dispatchTouchEvent(MotionEvent ev) {}        @Override        public boolean dispatchTrackballEvent(MotionEvent ev) {}         @Override        public boolean dispatchGenericMotionEvent(MotionEvent ev) {}        @Override        public boolean onTouchEvent(MotionEvent event) {            return onInterceptTouchEvent(event);        }        @Override        public boolean onInterceptTouchEvent(MotionEvent event) {...}    }}

三个重要的事件分发方法:

1、dispatchTouchEvent

2、onInterceptTouchEvent

3、onTouchEvent

需要注意的是:Activity和View是没有第二个方法的,因为Activity是作为事件的最原始分发者,如果Activity拦截了这个事件,就会导致整个屏幕都无法响应事件。而View作为事件传递的最末端,要么消费这个事件要么把这个事件回传,是不需要有这个方法的。

事件分发流程:

Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View

 

 

更多相关文章

  1. Android(安卓)SDK安装失败处理办法
  2. Android(安卓)中如何处理双击事件
  3. Android(安卓)之多点触摸 手势拉伸放大缩小图片 并在ImageView上
  4. Android高仿网易新闻客户端之动态添加标签
  5. [置顶] Android中的dispatchTouchEvent()、onInterceptTouchEven
  6. Android(安卓)SDK安装失败处理办法
  7. Android实验法分析Touch事件传递
  8. android音频处理
  9. Android-防止事件导致的oncreate的多次调用

随机推荐

  1. 在Linux(Ubuntu)下安装Android设备的ADB/US
  2. android学习目标计划和安排 2012-5-4 to
  3. Android(安卓)2.0以后的Contacts API--Co
  4. android登录Web以及登录保持,cookie管理相
  5. [Android]ActivityUnitTestCase解释
  6. 初学android:四大组件之contentprovider
  7. android APN切换cmwap实现
  8. 我的第一个Android应用
  9. monkeyrunner官方文档翻译
  10. 原生 iOS 项目集成 React Native