Android事件分发机制详解

Android事件分发机制 最近在学习安卓的事件分发机制,实际上到目前为止还没有写过自定义View,在自定义View中如果需要能够响应手指的点击或者滑动事件就需要处理事件的分发,否则很容易出现冲突。写这篇博客是为了让自己能够更好的消化吸收,同时以后忘了能够很快的捡回来。此篇主要参考。 阅读详情

转载:https://www.jianshu.com/p/38015afcdb58

参考:

  • https://www.cnblogs.com/fuly550871915/p/4983682.html
  • https://www.jianshu.com/p/82964e524c8e

建立模型:

拟人化来理解android中的事件机制

其实android中的事件传递与处理机制跟我们生活中的事件处理是一样的。这里有一个生活中的例子,很能说明这个问题。阐述如下:

你是一个公司的员工,
你的上头有一个主管,主管上头呢还有一个经理。
为了简单,你们这个团队就有这三个人。

那么如果上头安排一件事下来要处理,流程是怎样的呢?
显然应该是由你的经理将这件事安排给你的主管来处理,
你的主管再将这件事安排给你来处理。
等你把这件事办好了,你就应该给你的主管报告,再由你的主管来向你的经理
报告。
显然,你的主管和经理也有处理这件事的权限,
如果他们觉得事情很复杂,你办不了,或者他们比较照顾下级,可能就自己把这件事给办了,这个时候这件事就不会再传递给下一级来处理了。
这个事件处理的过程,是不是太容易理解了!

其实android中的事件处理流程就是跟生活中的事件处理是一样的。

比如你在ViewGroupA中嵌套了一个VewiGroupB,然后又在ViewGroupB

中嵌套了一个MyView。那么一个触摸事件传递过来,会发生什么情况呢?类比上面的公司员工的处理事件,显然会发生下面的过程:

触摸事件传递过来后,
1.ViewGroupA一看自己里面还有一个员工可以利用,就是ViewGroupB,
那不用白不用,就会把这个事件传递给ViewGroupB,告诉他,你给我把这个事件处理了!
2.ViewGroupB呢一看,我不怕,我里面也有一个员工就是MyView,它得给我干活,于是又会把这个事件传递给MyView,让它来处理。
3.MyView一看,没办法啊,我手底下没有员工了,那怎么办,我只能自己处理了(前提是它有处理这个事件的能力),所以就把这个触摸事件给处理了。
4.处理完成后呢?MyView就是给ViewGroupB报告,我已经把事情办好了,你来审核一下
,看看办理的咋样。ViewGroupB一审核,觉得不错,就再将结果呈现给ViewGroupA。ViewGroupA再审核,通过了才算通过。

在这个过程中,也可能出现几种情况:
(1)MyView说,完蛋了,这事我的能力办不好啊,于是就向VeiwGroupB报告,我没有处理,请你来处理,你是我上司,能力比我强。于是ViewGroupB就会来帮忙处理。当然了,
    如果ViewGroupB也没能力处理,那就只能反馈给VeiwGroupA,让它来消化这个事件。
(2)也可能MyView处理非常完美,向ViewGroupB一报告,
ViewGroupB一开心就说不用再交给ViewGroupA审核了,我担保通过,于是事件到此直接终止。

上面用很形象的话来讲adnroid中的事件传递和处理机制讲解了一下。android用下面的几个方法将上面的过程完美封装了:


在ViewGroup中,有下面三个方法:
(1)dispatchTouchEvent     该方法用来分发事件,一般不会重写这个方法
(2)onInterceptTouchEvent  用来拦截事件
(3)onTouchEvent           用来处理事件

而View中,只有两个方法,即:
(1)dispatchTouchEvent     该方法用来分发事件,一般不会重写这个方法
(2)onTouchEvent           用来处理事件

结论:

  • dispatchTouchEvent()、 onTouchEvent() 消费事件、终结事件传递(返回true)
  • 而onInterceptTouchEvent
    • 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,对后续的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用
    • 请记住:接收了ACTION_DOWN事件的函数不一定能收到后续事件(ACTION_MOVE、ACTION_UP)

https://upload-images.jianshu.io/upload_images/545982-e1b0bea3b5de2844.png?imageMogr2/auto-orient/strip|imageView2/2/w/1158

1. 基础认知

1.1 事件分发的对象是谁?

答:点击事件(Touch事件)

  • 定义
    当用户触摸屏幕时(View 或 ViewGroup派生的控件),将产生点击事件(Touch事件)
    Touch事件的相关细节(发生触摸的位置、时间等)被封装成MotionEvent对象
  • 事件类型(4种)
事件类型具体动作
MotionEvent.ACTION_DOWN按下View(所有事件的开始)
MotionEvent.ACTION_UP抬起View(与DOWN对应)
MotionEvent.ACTION_MOVE滑动View
MotionEvent.ACTION_CANCEL结束事件(非人为原因)
  • 特别说明:事件列
    从手指接触屏幕 至 手指离开屏幕,这个过程产生的一系列事件

1.2 事件分发的本质

答:将点击事件(MotionEvent)传递到某个具体的View & 处理的整个过程
即 事件传递的过程 = 分发过程。

https://upload-images.jianshu.io/upload_images/944365-02c588300f6ad741.png?imageMogr2/auto-orient/strip|imageView2/2

1.3 事件在哪些对象之间进行传递?

答:Activity、ViewGroup、View

1.4 事件分发的顺序

即 事件传递的顺序:Activity -> ViewGroup -> View

https://upload-images.jianshu.io/upload_images/944365-7fee82bba19a3821.png?imageMogr2/auto-orient/strip|imageView2/2/w/505

1.5 事件分发过程由哪些方法协作完成?

答:dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

https://upload-images.jianshu.io/upload_images/944365-7c6642f518ffa3d2.png?imageMogr2/auto-orient/strip|imageView2/2/w/675

1.6 总结

https://upload-images.jianshu.io/upload_images/944365-d0a7e6f3c2bbefcc.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

  • 至此,相信大家已经对 Android的事件分发有了感性的认知
  • 下面,我将详细介绍Android事件分发机制

2. 事件分发机制 源码分析

请谨记:Android事件分发流程 = Activity -> ViewGroup -> View

即:1个点击事件发生后,事件先传到Activity、再传到ViewGroup、最终再传到 View

  • 从上可知,要想充分理解Android分发机制,本质上是要理解:
    1. Activity对点击事件的分发机制
    2. ViewGroup对点击事件的分发机制
    3. View对点击事件的分发机制
  • 下面,我将通过源码,全面解析 事件分发机制
    即按顺序讲解:Activity事件分发机制、ViewGroup事件分发机制、View事件分发机制

2.1 Activity的事件分发机制

当一个点击事件发生时,事件最先传到Activity的dispatchTouchEvent()进行事件分发

2.1.1 源码分析

/**
  * 源码分析:Activity.dispatchTouchEvent()
  */ 
    public boolean dispatchTouchEvent(MotionEvent ev) {

            // 一般事件列开始都是DOWN事件 = 按下事件,故此处基本是true
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {

                onUserInteraction();
                // ->>分析1
            }

            // ->>分析2
            if (getWindow().superDispatchTouchEvent(ev)) {

                return true;
                // 若getWindow().superDispatchTouchEvent(ev)的返回true
                // 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束
                // 否则:继续往下调用Activity.onTouchEvent

            }
            // ->>分析4
            return onTouchEvent(ev);
        }


/**
  * 分析1:onUserInteraction()
  * 作用:实现屏保功能
  * 注:
  *    a. 该方法为空方法
  *    b. 当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
  */
      public void onUserInteraction() { 

      }
      // 回到最初的调用原处

/**
  * 分析2:getWindow().superDispatchTouchEvent(ev)
  * 说明:
  *     a. getWindow() = 获取Window类的对象
  *     b. Window类是抽象类,其唯一实现类 = PhoneWindow类;即此处的Window类对象 = PhoneWindow类对象
  *     c. Window类的superDispatchTouchEvent() = 1个抽象方法,由子类PhoneWindow类实现
  */
    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {

        return mDecor.superDispatchTouchEvent(event);
        // mDecor = 顶层View(DecorView)的实例对象
        // ->> 分析3
    }

/**
  * 分析3:mDecor.superDispatchTouchEvent(event)
  * 定义:属于顶层View(DecorView)
  * 说明:
  *     a. DecorView类是PhoneWindow类的一个内部类
  *     b. DecorView继承自FrameLayout,是所有界面的父类
  *     c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup
  */
    public boolean superDispatchTouchEvent(MotionEvent event) {

        return super.dispatchTouchEvent(event);
        // 调用父类的方法 = ViewGroup的dispatchTouchEvent()
        // 即 将事件传递到ViewGroup去处理,详细请看ViewGroup的事件分发机制

    }
    // 回到最初的调用原处

/**
  * 分析4:Activity.onTouchEvent()
  * 定义:属于顶层View(DecorView)
  * 说明:
  *     a. DecorView类是PhoneWindow类的一个内部类
  *     b. DecorView继承自FrameLayout,是所有界面的父类
  *     c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup
  */
  public boolean onTouchEvent(MotionEvent event) {

        // 当一个点击事件未被Activity下任何一个View接收 / 处理时
        // 应用场景:处理发生在Window边界外的触摸事件
        // ->> 分析5
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        
        return false;
        // 即 只有在点击事件在Window边界外才会返回true,一般情况都返回false,分析完毕
    }

/**
  * 分析5:mWindow.shouldCloseOnTouch(this, event)
  */
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
    // 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
    if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
            && isOutOfBounds(context, event) && peekDecorView() != null) {
        return true;
    }
    return false;
    // 返回true:说明事件在边界外,即 消费事件
    // 返回false:未消费(默认)
}
// 回到分析4调用原处

2.1.2 总结

当一个点击事件发生时,从Activity的事件分发开始(Activity.dispatchTouchEvent())

https://upload-images.jianshu.io/upload_images/944365-f8fda76bbdad7b96.png?imageMogr2/auto-orient/strip|imageView2/2/w/590

  • 方法总结
    https://upload-images.jianshu.io/upload_images/944365-e186b0edcb590546.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

那么,ViewGroup的dispatchTouchEvent()什么时候返回true / false?请继续往下看ViewGroup事件的分发机制

2.2 ViewGroup事件的分发机制

从上面Activity事件分发机制可知,ViewGroup事件分发机制从dispatchTouchEvent()开始

2.2.1 源码分析

  1. Android 5.0后,ViewGroup.dispatchTouchEvent()的源码发生了变化(更加复杂),但原理相同;
  2. 本文为了让读者容易理解,故采用Android 5.0前的版本
/**
  * 源码分析:ViewGroup.dispatchTouchEvent()
  */ 
    public boolean dispatchTouchEvent(MotionEvent ev) { 

    ... // 仅贴出关键代码

        // 重点分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
            if (disallowIntercept || !onInterceptTouchEvent(ev)) {  

            // 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改
            // 判断值2: !onInterceptTouchEvent(ev) = 对onInterceptTouchEvent()返回值取反
                    // a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
                    // b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
                    // c. 关于onInterceptTouchEvent() ->>分析1

                ev.setAction(MotionEvent.ACTION_DOWN);  
                final int scrolledXInt = (int) scrolledXFloat;  
                final int scrolledYInt = (int) scrolledYFloat;  
                final View[] children = mChildren;  
                final int count = mChildrenCount;  

        // 重点分析2
            // 通过for循环,遍历了当前ViewGroup下的所有子View
            for (int i = count - 1; i >= 0; i--) {  
                final View child = children[i];  
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                        || child.getAnimation() != null) {  
                    child.getHitRect(frame);  

                    // 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
                    // 若是,则进入条件判断内部
                    if (frame.contains(scrolledXInt, scrolledYInt)) {  
                        final float xc = scrolledXFloat - child.mLeft;  
                        final float yc = scrolledYFloat - child.mTop;  
                        ev.setLocation(xc, yc);  
                        child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  

                        // 条件判断的内部调用了该View的dispatchTouchEvent()
                        // 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面的View事件分发机制)
                        if (child.dispatchTouchEvent(ev))  { 

                        mMotionTarget = child;  
                        return true; 
                        // 调用子View的dispatchTouchEvent后是有返回值的
                        // 若该控件可点击,那么点击时,dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立
                        // 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
                        // 即把ViewGroup的点击事件拦截掉

                                }  
                            }  
                        }  
                    }  
                }  
            }  
            boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||  
                    (action == MotionEvent.ACTION_CANCEL);  
            if (isUpOrCancel) {  
                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;  
            }  
            final View target = mMotionTarget;  

        // 重点分析3
        // 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复写onInterceptTouchEvent(),从而让其返回true)
        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);
            // 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
            // 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())
            // 此处需与上面区别:子View的dispatchTouchEvent()
        } 

        ... 

}
/**
  * 分析1:ViewGroup.onInterceptTouchEvent()
  * 作用:是否拦截事件
  * 说明:
  *     a. 返回true = 拦截,即事件停止往下传递(需手动设置,即复写onInterceptTouchEvent(),从而让其返回true)
  *     b. 返回false = 不拦截(默认)
  */
  public boolean onInterceptTouchEvent(MotionEvent ev) {  
    
    return false;

  } 
  // 回到调用原处

2.2.2 总结

  • 结论:Android事件分发总是先传递到ViewGroup、再传递到View
  • 过程:当点击了某个控件时

https://upload-images.jianshu.io/upload_images/944365-6ec2e864af7ffd37.png?imageMogr2/auto-orient/strip|imageView2/2

  • 核心方法总结
    https://upload-images.jianshu.io/upload_images/944365-ff627fea1a2244ad.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

2.2.3 Demo讲解

  • 布局如下

https://upload-images.jianshu.io/upload_images/944365-b0bf3dd7ad41b335.png?imageMogr2/auto-orient/strip|imageView2/2/w/210

  • 测试代码

布局文件:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:focusableInTouchMode="true"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮1" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮2" />

</LinearLayout>

核心代码:MainActivity.java

/**
  * ViewGroup布局(myLayout)中有2个子View = 2个按钮
  */
    public class MainActivity extends AppCompatActivity {

    Button button1,button2;
    ViewGroup myLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button1 = (Button)findViewById(R.id.button1);
        button2 = (Button)findViewById(R.id.button2);
        myLayout = (LinearLayout)findViewById(R.id.my_layout);

        // 1.为ViewGroup布局设置监听事件
        myLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("TAG", "点击了ViewGroup");
            }
        });

        // 2. 为按钮1设置监听事件
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("TAG", "点击了button1");
            }
        });

        // 3. 为按钮2设置监听事件
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("TAG", "点击了button2");
            }
        });

    }
}
  • 结果测试
    https://upload-images.jianshu.io/upload_images/944365-a9c45aa25d12b589.png?imageMogr2/auto-orient/strip|imageView2/2/w/987

从上面的测试结果发现:

  • 点击Button时,执行Button.onClick(),但ViewGroupLayout注册的onTouch()不会执行
  • 只有点击空白区域时,才会执行ViewGroupLayout的onTouch()
  • 结论:Button的onClick()将事件消费掉了,因此事件不会再继续向下传递。

2.3 View事件的分发机制

从上面ViewGroup事件分发机制知道,View事件分发机制从dispatchTouchEvent()开始

2.3.1 源码分析

/**
  * 源码分析:View.dispatchTouchEvent()
  */
  public boolean dispatchTouchEvent(MotionEvent event) {  

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
                mOnTouchListener.onTouch(this, event)) {  
            return true;  
        } 
        return onTouchEvent(event);  
  }
  // 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()
  //     1. mOnTouchListener != null
  //     2. (mViewFlags & ENABLED_MASK) == ENABLED
  //     3. mOnTouchListener.onTouch(this, event)
  // 下面对这3个条件逐个分析


/**
  * 条件1:mOnTouchListener != null
  * 说明:mOnTouchListener变量在View.setOnTouchListener()方法里赋值
  */
  public void setOnTouchListener(OnTouchListener l) { 

    mOnTouchListener = l;  
    // 即只要我们给控件注册了Touch事件,mOnTouchListener就一定被赋值(不为空)
        
} 

/**
  * 条件2:(mViewFlags & ENABLED_MASK) == ENABLED
  * 说明:
  *     a. 该条件是判断当前点击的控件是否enable
  *     b. 由于很多View默认enable,故该条件恒定为true
  */

/**
  * 条件3:mOnTouchListener.onTouch(this, event)
  * 说明:即 回调控件注册Touch事件时的onTouch();需手动复写设置,具体如下(以按钮Button为例)
  */
    button.setOnTouchListener(new OnTouchListener() {  
        @Override  
        public boolean onTouch(View v, MotionEvent event) {  
     
            return false;  
        }  
    });
    // 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束
    // 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)

接下来,我们继续看:onTouchEvent(event)的源码分析

详情请看注释
Android 5.0后 View.onTouchEvent()源码发生了变化(更加复杂),但原理相同;
本文为了让读者更好理解,所以采用Android 5.0前的版本
/**
  * 源码分析:View.onTouchEvent()
  */
  public boolean onTouchEvent(MotionEvent event) {  
    final int viewFlags = mViewFlags;  

    if ((viewFlags & ENABLED_MASK) == DISABLED) {  
         
        return (((viewFlags & CLICKABLE) == CLICKABLE ||  
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
    }  
    if (mTouchDelegate != null) {  
        if (mTouchDelegate.onTouchEvent(event)) {  
            return true;  
        }  
    }  

    // 若该控件可点击,则进入switch判断中
    if (((viewFlags & CLICKABLE) == CLICKABLE ||  
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  

                switch (event.getAction()) { 

                    // a. 若当前的事件 = 抬起View(主要分析)
                    case MotionEvent.ACTION_UP:  
                        boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  

                            ...// 经过种种判断,此处省略

                            // 执行performClick() ->>分析1
                            performClick();  
                            break;  

                    // b. 若当前的事件 = 按下View
                    case MotionEvent.ACTION_DOWN:  
                        if (mPendingCheckForTap == null) {  
                            mPendingCheckForTap = new CheckForTap();  
                        }  
                        mPrivateFlags |= PREPRESSED;  
                        mHasPerformedLongPress = false;  
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  
                        break;  

                    // c. 若当前的事件 = 结束事件(非人为原因)
                    case MotionEvent.ACTION_CANCEL:  
                        mPrivateFlags &= ~PRESSED;  
                        refreshDrawableState();  
                        removeTapCallback();  
                        break;

                    // d. 若当前的事件 = 滑动View
                    case MotionEvent.ACTION_MOVE:  
                        final int x = (int) event.getX();  
                        final int y = (int) event.getY();  
        
                        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;  
                }  
                // 若该控件可点击,就一定返回true
                return true;  
            }  
             // 若该控件不可点击,就一定返回false
            return false;  
        }

/**
  * 分析1:performClick()
  */  
    public boolean performClick() {  

        if (mOnClickListener != null) {  
            playSoundEffect(SoundEffectConstants.CLICK);  
            mOnClickListener.onClick(this);  
            return true;  
            // 只要我们通过setOnClickListener()为控件View注册1个点击事件
            // 那么就会给mOnClickListener变量赋值(即不为空)
            // 则会往下回调onClick() & performClick()返回true
        }  
        return false;  
    }  

2.3.2 总结

  • 每当控件被点击时:
    https://upload-images.jianshu.io/upload_images/944365-76ce9e8299386729.png?imageMogr2/auto-orient/strip|imageView2/2/w/590
  • 注:onTouch()的执行 先于 onClick()
  • 核心方法总结

https://upload-images.jianshu.io/upload_images/944365-762cf45f36858bbd.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

2.3.3 Demo讲解

下面我将用Demo验证上述的结论

/**
  * 结论验证1:在回调onTouch()里返回false
  */
   // 1. 通过OnTouchListener()复写onTouch(),从而手动设置返回false
   button.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                System.out.println("执行了onTouch(), 动作是:" + event.getAction());
           
                return false;
            }
        });

    // 2. 通过 OnClickListener()为控件设置点击事件,为mOnClickListener变量赋值(即不为空),从而往下回调onClick()
    button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                System.out.println("执行了onClick()");
            }

        });

/**
  * 结论验证2:在回调onTouch()里返回true
  */
   // 1. 通过OnTouchListener()复写onTouch(),从而手动设置返回true
   button.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                System.out.println("执行了onTouch(), 动作是:" + event.getAction());
           
                return true;
            }
        });

    // 2. 通过 OnClickListener()为控件设置点击事件,为mOnClickListener变量赋值(即不为空)
    // 但由于dispatchTouchEvent()返回true,即事件不再向下传递,故不调用onClick())
    button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                System.out.println("执行了onClick()");
            }
            
        });

  • 测试结果

https://upload-images.jianshu.io/upload_images/944365-97959093583369a0.png?imageMogr2/auto-orient/strip|imageView2/2/w/904

2.4 总结

https://upload-images.jianshu.io/upload_images/944365-eeebede55f55b040.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

若您已经看到此处,那么恭喜你,你已经能非常熟悉掌握Android的事件分发机制了

即:Activity、ViewGroup、View 的事件分发机制

3. 工作流程 总结

  • 在本节中,我将结合源码,梳理出1个事件分发的工作流程总结,具体如下:

ttps://upload-images.jianshu.io/upload_images/944365-aea821bbb613c195.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

    左侧虚线:具备相关性 & 逐层返回
  • 以角色为核心的图解说明

https://upload-images.jianshu.io/upload_images/944365-bccafd3ff8a880ff.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

  • 以方法为核心的图解说明
    https://upload-images.jianshu.io/upload_images/944365-9f340a39bdad520e.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

4. 核心方法总结

  • 已知事件分发过程的核心方法为:dispatchTouchEvent()、onInterceptTouchEvent() 和 onTouchEvent()
    https://upload-images.jianshu.io/upload_images/944365-faaf73d0f3eb870f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

  • 下面,我将结合总结的工作流程,再次详细讲解该3个方法

4.1 dispatchTouchEvent()

  • 简介
    https://upload-images.jianshu.io/upload_images/944365-4fbf11afa24b033b.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

https://upload-images.jianshu.io/upload_images/944365-f8888a622c255648.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

  • 返回情况说明

情况1:返回true

https://upload-images.jianshu.io/upload_images/944365-36af591c11a8e450.png?imageMogr2/auto-orient/strip|imageView2/2/w/824

https://upload-images.jianshu.io/upload_images/944365-aef94c6c182353a9.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

情况2:false(默认)

https://upload-images.jianshu.io/upload_images/944365-ea06029e3176635f.png?imageMogr2/auto-orient/strip|imageView2/2/w/926

https://upload-images.jianshu.io/upload_images/944365-299cfcbe3a9c9fd5.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

4.3 onTouchEvent()

简介

https://upload-images.jianshu.io/upload_images/944365-b5f527027257b98e.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

https://upload-images.jianshu.io/upload_images/944365-744f5b7e8d413562.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

  • 返回情况说明

情况1:返回true

https://upload-images.jianshu.io/upload_images/944365-36af591c11a8e450.png?imageMogr2/auto-orient/strip|imageView2/2/w/824

情况2:返回false(default)
https://upload-images.jianshu.io/upload_images/944365-efd21a46a9af808e.png?imageMogr2/auto-orient/strip|imageView2/2/w/824

4.4 三者关系

下面,我用一段伪代码来阐述上述3个方法的关系 & 事件传递规则

/**
  * 点击事件产生后
  */ 
  // 步骤1:调用dispatchTouchEvent()
  public boolean dispatchTouchEvent(MotionEvent ev) {

    boolean consume = false; //代表 是否会消费事件
    
    // 步骤2:判断是否拦截事件
    if (onInterceptTouchEvent(ev)) {
      // a. 若拦截,则将该事件交给当前View进行处理
      // 即调用onTouchEvent ()方法去处理点击事件
        consume = onTouchEvent (ev) ;

    } else {

      // b. 若不拦截,则将该事件传递到下层
      // 即 下层元素的dispatchTouchEvent()就会被调用,重复上述过程
      // 直到点击事件被最终处理为止
      consume = child.dispatchTouchEvent (ev) ;
    }

    // 步骤3:最终返回通知 该事件是否被消费(接收 & 处理)
    return consume;

   }

5. 常见的事件分发场景

下面,我将通过实例说明常见的事件传递情况 & 流程

5.1 背景描述

讨论的布局如下:

https://upload-images.jianshu.io/upload_images/944365-e0f526dd1b5731be.png?imageMogr2/auto-orient/strip|imageView2/2

  • 情景
  1. 用户先触摸到屏幕上View C上的某个点(图中黄区)
  2. Action_DOWN事件在此处产生
  3. 用户移动手指
  4. 最后离开屏幕

5.2 一般的事件传递情况

一般的事件传递场景有:

  • 默认情况
  • 处理事件
  • 拦截DOWN事件
  • 拦截后续事件(MOVE、UP)

场景1:默认

  • 即不对控件里的方法(dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent())进行重写 或 更改返回值
  • 那么调用的是这3个方法的默认实现:调用下层的方法 & 逐层返回
    事件传递情况:(呈U型)
  1. 从上往下调用dispatchTouchEvent()
    • Activity A ->> ViewGroup B ->> View C
  2. 从下往上调用onTouchEvent()
    • View C ->> ViewGroup B ->> Activity A

https://upload-images.jianshu.io/upload_images/944365-161a6e6fc8723248.png?imageMogr2/auto-orient/strip|imageView2/2

注:虽然ViewGroup B的onInterceptTouchEvent()对DOWN事件返回了false,

但后续的事件(MOVE、UP)依然会传递给它的onInterceptTouchEvent()
这一点与onTouchEvent()的行为是不一样的:不再传递 & 接收该事件列的其他事件

场景2:处理事件

设View C希望处理该点击事件,即:设置View C为可点击的(Clickable) 或 复写其onTouchEvent()返回true

最常见的:设置Button按钮来响应点击事件

事件传递情况:(如下图)

DOWN事件被传递给C的onTouchEvent方法,该方法返回true,表示处理该事件
因为View C正在处理该事件,那么DOWN事件将不再往上传递给ViewGroup B 和 Activity A的onTouchEvent();

该事件列的其他事件(Move、Up)也将传递给View C的onTouchEvent()

https://upload-images.jianshu.io/upload_images/944365-77e933eb44682777.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

会逐层往dispatchTouchEvent() 返回,最终事件分发结束

场景3:拦截DOWN事件

假设ViewGroup B希望处理该点击事件,即ViewGroup B复写了onInterceptTouchEvent()返回true、onTouchEvent()返回true

  • 事件传递情况:(如下图)
  1. DOWN事件被传递给ViewGroup

  2. B的onInterceptTouchEvent(),该方法返回true,表示拦截该事件,即自己处理该事件(事件不再往下传递)

  3. 调用自身的onTouchEvent()处理事件(DOWN事件将不再往上传递给Activity A的onTouchEvent())

该事件列的其他事件(Move、Up)将直接传递给ViewGroup B的onTouchEvent()

  • 注:

该事件列的其他事件(Move、Up)将不会再传递给ViewGroup B的onInterceptTouchEvent();

因:该方法一旦返回一次true,就再也不会被调用
逐层往dispatchTouchEvent() 返回,最终事件分发结束

https://upload-images.jianshu.io/upload_images/944365-a5e7cfed2cba02c3.png?imageMogr2/auto-orient/strip|imageView2/2/w/1140

场景4:拦截DOWN的后续事件

  • 结论
  1. 若 ViewGroup 拦截了一个半路的事件(如MOVE),
  2. 该事件将会被系统变成一个CANCEL事件 & 传递给之前处理该事件的子View;
    该事件不会再传递给ViewGroup 的onTouchEvent()
  3. 只有再到来的事件才会传递到ViewGroup的onTouchEvent()

场景描述

  1. ViewGroup B 无拦截DOWN事件

(还是View C来处理DOWN事件),但它拦截了接下来的MOVE事件

  1. 即 DOWN事件传递到View C的onTouchEvent(),返回了true
    实例讲解

  2. 在后续到来的MOVE事件,ViewGroup B 的onInterceptTouchEvent()返回true拦截该MOVE事件,

  3. 但该事件并没有传递给ViewGroup B ;

  4. 这个MOVE事件将会被系统变成一个CANCEL事件传递给View C的onTouchEvent()

  5. 后续又来了一个MOVE事件,该MOVE事件才会直接传递给ViewGroup B 的onTouchEvent()

  6. 后续事件将直接传递给ViewGroup B 的onTouchEvent()处理,而不会再传递给ViewGroup B 的onInterceptTouchEvent(),因该方法一旦返回一次true,就再也不会被调用了。

  7. View C再也不会收到该事件列产生的后续事件

https://upload-images.jianshu.io/upload_images/944365-1599f532038686cd.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200
至此,关于Android常见的事件传递情况 & 流程已经讲解完毕。

6. 额外知识

6.1 Touch事件的后续事件(MOVE、UP)层级传递

  • 若给控件注册了Touch事件,每次点击都会触发一系列action事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP等)
  • 当dispatchTouchEvent()事件分发时,只有前一个事件(如ACTION_DOWN)返回true,才会收到后一个事件(ACTION_MOVE和ACTION_UP)
    • 即如果在执行ACTION_DOWN时返回false,后面一系列的ACTION_MOVE、ACTION_UP事件都不会执行

从上面对事件分发机制分析知:

  • dispatchTouchEvent()、 onTouchEvent() 消费事件、终结事件传递(返回true)
  • 而onInterceptTouchEvent 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,对后续的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用
    • 请记住:接收了ACTION_DOWN事件的函数不一定能收到后续事件(ACTION_MOVE、ACTION_UP)

这里给出ACTION_MOVE和ACTION_UP事件的传递结论:

  • 结论1

    若对象(Activity、ViewGroup、View)的dispatchTouchEvent()分发事件后消费了事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP

    • 黑线:ACTION_DOWN事件传递方向
    • 红线:ACTION_MOVE 、 ACTION_UP事件传递方向

https://upload-images.jianshu.io/upload_images/944365-93d0b1496e9e6ca4.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

  • 结论2

    若对象(Activity、ViewGroup、View)的onTouchEvent()处理了事件(返回true),那么ACTION_MOVE、ACTION_UP的事件从上往下传到该View后就不再往下传递,而是直接传给自己的onTouchEvent()& 结束本次事件传递过程。

    • 黑线:ACTION_DOWN事件传递方向
    • 红线:ACTION_MOVE、ACTION_UP事件传递方向

https://upload-images.jianshu.io/upload_images/944365-9d639a0b9ebf7b4a.png?imageMogr2/auto-orient/strip|imageView2/2/w/1200

6.2 onTouch()和onTouchEvent()的区别

  • 该2个方法都是在View.dispatchTouchEvent()中调用
  • 但onTouch()优先于onTouchEvent执行;若手动复写在onTouch()中返回true(即 将事件消费掉),将不会再执行onTouchEvent()
    • 注:若1个控件不可点击(即非enable),那么给它注册onTouch事件将永远得不到执行,具体原因看如下代码
// &&为短路与,即如果前面条件为false,将不再往下执行
//  故:onTouch()能够得到执行需2个前提条件:
     // 1. mOnTouchListener的值不能为空
     // 2. 当前点击的控件必须是enable的
mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)

// 对于该类控件,若需监听它的touch事件,就必须通过在该控件中重写onTouchEvent()来实现

其他问题:

  • 为什么事件传递是从activity传到view,而不是从view往window层传递?

    应该是 activity - > phoneWindow -> decorView -> contentView

  • 从Activity->ViewGroup->View 这个过程真的没问题吗?更加微观过程:

    ViewRootImpl.WindowInputReceiver
    ->View.dispatchPointerEvent
    ->DecorView.dispatchTouchEvent
    ->PhoneWindow.dispatchTouchEvent||super.dispatchTouchEvent(ev)
  1. 对于如果onIntercept返回true拦截了,交给onTouchEvent去处理,具体体现在何处?
   请看ViewGroup事件分发机制源码dispatchTouchEvent()
   里最后语句:return super.dispatchTouchEvent(ev),
   此处调用ViewGroup父类的dispatchTouchEvent() = View.dispatchTouchEvent(),
   其内部最终会调用onTouchEvent()
  1. 判断子View是否能够接收事件从哪里体现,可以详细一点
   请看View事件分发机制源码里onTouchEvent()里的performClick()
  1. viewGroup和view的dispatch返回false,会直接回溯到parent的onTouchEvent?
viewGroup.dispatch()返回false的原因 :
    ViewGroup父类的dispatchTouchEvent()返回false 
View.dispatch()返回false的原因:
    而View.dispatch()返回false后,
    会跳出执行ViewGroup父类的dispatchTouchEvent()。。。
    
  1. View 执行onTouchEvent()返回false后续?
View.onTouchEvent()返回false后,
从而导致View.dispatchTouchEvevt()返回false,
从而导致ViewGroup.dispatchTouchEvevt()里返回super.dispatchTouchEvevt(),
最终调用ViewGroup的onTouch() ->>onTouchEvent() ->> performClick() ->> onClick()
Android 事件分发机制详解/ 及Activity启动流程浅谈 目前网上的事件分发机制文章很多,可能大家看了很多,还是很懵逼,这篇文章是我自己总结的事件分发机制,提取了在事件分发机制中,最精华的部分,如果对事件分发还是不太清楚,可以看看这篇文章。 阅读详情

相关推荐

Android 事件分发机制详解(上)

Android事件分发机制是Android开发者必须了解的基础。当一个点击事件发生时,从Activity的事件分发开始(主要包括:dispatchTouchEvent()、onTouchEvent() 总结如下:那么ViewGroup的什么时候返回true / false?请继续往下看ViewGroup事件的分发机制。Android事件分发传递到Acitivity后,总是先传递到ViewGroup、再传递到View。流程总结如下:(假设已经经过了Acitivity事件分发传递并传递到ViewGroup。

u010345983的博客 5263

Android事件分发机制【最新、最全、最易懂、由浅入深】

Android事件分发流程 = Activity -> ViewGroup -> View即:1个点击事件发生后,事件先传到Activity、再传到ViewGroup、最终再传到ViewActivity对点击事件的分发机制ViewGroup对点击事件的分发机制View对点击事件的分发机制Activity事件分发机制、ViewGroup事件分发机制、View事件分发机制当一个点击事件发生时,从Activity的事件分发开始(),流程总结如下:三者介绍。

丨康有为丨的博客 1万+

Git Worktree + Claude Code:多终端并发开发完全实战

深入讲解如何使用 Git Worktree 配合多终端 Claude Code 实现并发开发,通过实战案例展示如何在 Android 开发中提升 2-3 倍效率,彻底告别等待 AI 的问题。

Cheson的专栏 1202

Android事件分发机制详解:史上最全面、最易懂

点击事件由哪个对象发出,经过哪些对象,最终达到哪个对象并最终得到处理。这里的对象是指Activity、ViewGroup、View事件分发过程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三个方法协助完成经过上述3个问题,相信大家已经对Android的事件分发有了感性的认知,接下来,我将详细介绍Android事件分发机制。

w1142203475的博客 1万+

Splunk CVE-2018-11409认证绕过漏洞深度解析

Splunk作为主流企业级日志分析平台,其Web管理接口的认证机制直接关系到整个安全运营体系的可信边界。CVE-2018-11409本质上是REST API路由层会话校验缺失引发的未授权访问问题,根源在于`/en-US/splunkd/__raw/`路径绕过了splunkweb与splunkd之间的信任链验证,导致配置文件、用户哈希、App源码等敏感信息明文泄露。该漏洞凸显了Web接口权限模型设计失守的风险,尤其在默认配置未加固、HTTPS未启用、初始密码未修改的生产环境中危害急剧放大。结合vulfocus

weixin_30875157的博客 597

并网逆变器学习笔记8---平衡桥(独立中线模块)控制

参考文献:《带独立中线模块的三相四线制逆变器中线电压脉动抑制方法》---赵文心。

圆桌骑士的博客 7304

安卓-Android事件分发机制详解

链接: 原帖跳转 如何提升安卓水平?安卓开发者必须了解的事件分发机制。 最全面、最易懂的形式来讲解Android事件分发机制。 0. 前言 鉴于安卓分发机制较为复杂,故分为多个层次进行讲解,分别为基础篇、实践篇与高级篇。 (一)基础篇:从基本概念入手,介绍了分发机制中的核心方法,通过分析其核心逻辑,总结其事件分发机制。 (二)实践篇:该篇设计了简单与复杂的两个demo样例,从现象与应用的角度去讲解分发机制的核心内容,帮助读者从另一个角度理解事件分发机制。 (三)高级篇:从源码角度去分析分发机

ruiruiddd的博客 840

Pydoll浏览器自动化框架教程

让浏览器自动化回归本质。不再纠缠驱动兼容、不再手动处理验证码、不再因为请求和 UI 分裂而头疼。如果你经常做 Web 自动化、爬虫或者测试,不妨试试 Pydoll。也别忘了给项目点个 ⭐,让作者知道有人喜欢这个工具。👉GitHub 地址。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 204

Android 事件分发机制详解

更多内容请参照我的个人站点: http://stackvoid.com/ 网上很多关于Android事件分发机制的解释,大多数描述的都不够清晰,没有吧来龙去脉搞清楚,本文将带你从Touch事件产生到Touch事件被消费这一全过程作全面的剖析。 产生Touch事件 这部分牵扯到硬件和Linux内核部分;我们简单讲述一下这部分内容,如果有兴趣的话可以参考这篇文章。 传递Touc

Stackvoid小菜 1303

第12篇:TMC2240 CoolStep节能功能|软件配置+功耗测试(保姆级)

本文介绍了TMC2240驱动芯片的CoolStep节能功能,通过动态调节电机电流实现功耗优化。CoolStep利用StallGuard技术实时检测负载,自动调整运行电流,空载时可降低50%以上功耗。文章详细讲解了节能原理、寄存器配置方法和参数校准步骤,提供可直接复用的STM32代码。该功能特别适合电池供电设备,能显著延长续航时间,同时保持电机性能稳定。配置时需注意SG阈值、电流步长等关键参数,并可与静音模式叠加使用。

BackCatK Chen的博客 918

100天精通Python(可视化篇)——第98天:Pyecharts绘制多种炫酷日历图参数说明+代码实战

一、日历图介绍 二、基础配置 1. 配置项类 2. 添加函数 3. 日期标签配置 4. 坐标系配置 三、日历图实战 1. 基础日历图 2. 日历热图

努力让自己发光,对的人才能迎着光而来 2万+

Android事件的分发机制,android事件分发机制详解

1.事件的定义: 2.事件序列 3.事件分发的对象(1)Activity:控制生命周期 & 处理事件bash(2)ViewGroup:一组View的集合(含多个子View)ide(3)View:全部UI组件的基类布局4.事件分发的主要方法(1)dispatchTouchEvent(MotionEvent ev): 用来进行事件分发;ui(2)onInterceptTouchEvent(Mo...

weixin_39991926的博客 147

群辉NAS映射为本地盘符

鼠标右击网络→映射网络驱动器→以上适合通过局域网操作,采用ZeroTier组网后,也可以按照以上步骤实现,这时使用 “虚拟局域网”的IP地址。

dgnankai的专栏 5774

android事件分发机制的详解

android事件分发机制的详解 为什么要使用事件分发? ##在很多情况下,布局复杂的界面,由于有很多的控件重叠摆放在一起,这就出现了点击事件的重复性,在开发中经常遇到点击某个子控件,父控件的点击事件需要屏蔽掉,这时候就需要用到事件分发机制来协调处理。 事件分发机制的分发和消费方向 事件分发方向 事件分发方向:DecorView->phoneWindow->Activity->V...

weixin_42516109的博客 430

宇树G1二次开发资料汇总

宇树科技G1是一款29自由度的商业级人形机器人,配备NVIDIA Jetson Orin NX计算平台和完整SDK生态。本文系统介绍了G1的硬件架构、29个关节配置、传感器外设、双计算单元设计等关键信息,并详细解析了软件生态体系,包括C++/Python SDK、ROS2接口、仿真工具和AI框架。开发者可通过DDS通信实现高层运动控制或底层关节级精确控制,支持快速二次开发。文档提供了网络配置、接口调用示例等实用指南,帮助开发者高效利用该平台进行人形机器人应用开发。

m0_47999117的博客 1312

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Android触摸事件分发机制详解

触摸事件分发机制一直以来都是Android中比较重要的一大块,自定义view,各种复杂的自定义手势交互都与触摸事件分发机制关系密切,想要做好这些,就要对触摸事件了解透彻,并且需要不断的去实践来加深印象,否则在自己去实现的时候就会茫然不知所措,同时这个知识点也是面试必问的经典题目,所以说掌握它是必须的,今天就来详细分析一下整个触摸事件的分发流程和相关知识。(本文理论知识较多,比较枯燥,需要极大耐心观看~)

Water的博客 4987

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

(原创)Android事件分发机制详解

之前有些过一篇简单的博客 解决滑动冲突问题 今天对冲突背后的事件分发机制,做一个详细的介绍 下面开始! Android的事件分发机制相关的类: dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent() 分别用于事件分发、事件拦截和事件处理 一、事件分发的执行顺序流程图(默认情况下,不考虑事件拦截和处理) 如上图,事件的分发顺序由1开始依次分发到8。 事件分发的规则,隧道式下发。 在ViewGroup中如果有N..

Android_xiong_st的博客 5347

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Android开发——事件分发机制详解

0. 前言 深入学习事件分发机制,是为了解决在Android开发中遇到的滑动冲突问题做准备。事件分发机制描述了用户的手势一系列事件是如何被Android系统传递并消费的。首先对事件分发机制进行概述:如果当一个点击事件发生时,事件最先传递给当前Activity,再传递给Window,接着传递给顶级View,最后按照事件分发机制去分发事件。事件的传递过程可以用以下伪代码进行描述:...

关于Android开发的一些技术点总结 ╮( ̄▽ ̄”)╭ 1万+
上一篇: 锁 | 00 Java中的锁分类
下一篇: Okhttp 01 核心流程详解
阿杰、
博客等级 码龄11年 13粉丝 47原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值