精华内容
下载资源
问答
  • 在做的项目中,有一个要求就是需要用存储过程进行对数据进行一次性的插入操作。之前由于没接触过,也是弄的很头大。在网上看了还多资料,写的不详细,实用性不好,最后自己根据网上的总结出自己的一个大家都可以用的...

    前言:

    在做的项目中,有一个要求就是需要用存储过程进行对数据进行一次性的插入操作。之前由于没接触过,也是弄的很头大。在网上看了还多资料,写的不详细,实用性不好,最后自己根据网上的总结出自己的一个大家都可以用的。

    在正式开始之前先介绍下为啥用存储过程对数据进行操作。

    ①:效率高,只连接一次数据库,就可以完成所有的数据操作,不必进行循环一次次的连接数据库。

    ②:减少后代的代码量,直接调用存储过程就行。

    在解释下一个名词:临时表

    这个词我在做这功能之前我也没有用过。所谓临时表:它就是一个和我们数据库所建的表没有什么很大的区别。唯一的区别就是它是临时的,临时表存储在tempdb中,当不再使用时会自动删除。临时表有两种类型:本地和全局。它们在名称、可见性以及可用性上有区别。

    对于临时表有如下几个特点:

    本地临时表就是用户在创建表的时候添加了“#”前缀的表,其特点是根据数据库连接独立。只有创建本地临时表的数据库连接有表的访问权限,其它连接不能访问该表;
    不同的数据库连接中,创建的本地临时表虽然“名字”相同,但是这些表之间相互并不存在任何关系;在SQLSERVER中,通过特别的命名机制保证本地临时表在数据库连接上的独立性。
    真正的临时表利用了数据库临时表空间,由数据库系统自动进行维护,因此节省了表空间。并且由于临时表空间一般利用虚拟内存,大大减少了硬盘的I/O次数,因此也提高了系统效率。
    临时表在事务完毕或会话完毕数据自动清空,不必记得用完后删除数据。

    临时表在Sql Server中的创建的两种方式:

    --第一种
    create type tbAddUser as table
    (
    uAccount char(8),
    uPwd varchar(16),
    uName nvarchar(8),
    uAge int,
    uAddress nvarchar(64),
    uPhone char(11)
    )
    --第二种
    create table #tbUser
    (
    uAccount char(8),
    uPwd varchar(16),
    uName nvarchar(8),
    uAge int,
    uAddress nvarchar(64),
    uPhone char(11)
    )


    临时表在存储过程中的运用:

    create proc proAddUserTest
    @user as tbAddUser readonly--表变量,用于接受后台传递的表,传递给表变量(临时表)
    AS
    BEGIN 
    SET NOCOUNT ON;
    	insert into tbUserInfo select * from @user--将临时表中的数据插入到我们实际操作的表中
    	END
    select * from tbUserInfo

    VS里调用存储过程(传递表中数据)

            public DataTable GetTable()
            {
                //定义表结构要和数据库中表中的字段一致
                DataTable table = new DataTable();
                table.Columns.Add("userAccount", typeof(string));
                table.Columns.Add("userPwd", typeof(string));
                table.Columns.Add("userName", typeof(string));
                table.Columns.Add("userAge", typeof(int));
                table.Columns.Add("userAddress", typeof(string));
                table.Columns.Add("userPhone", typeof(string));
                //给各个列添加对应的表数据--一定要这样添加,其他的可能报错,原因我也不知道,哈哈
                table.Rows.Add("45678912", "000000", "罗三炮", 21,"商丘","14725836912");
                return table;
            }
            public void Add(DataSet userData)
            {
               
                    DataTable dtUser = GetTable();
                    //连接数据库。我用的是EF,大家如果用的是其他的,直接把连接数据库的con替换掉我的                                    就行
                    SqlConnection conn = (SqlConnection)db.Database.Connection;
                    conn.Open();
                    SqlCommand sqlcmd = conn.CreateCommand();
                    sqlcmd.CommandType = CommandType.StoredProcedure;
                    sqlcmd.CommandText = "proAddUserTest";//存储过程名
                    //@user存储过程里的表变量,dtUser需要传递的表
                    SqlParameter param = sqlcmd.Parameters.AddWithValue("@user", dtUser);
                    SqlDataReader sdr = sqlcmd.ExecuteReader();
                    conn.Close();
                        
            }

    执行效果:

    结尾:

           分享:努力成功之后的喜悦,是你最好的兴奋剂!

    展开全文
  • JAVA 参数传递

    千次阅读 2013-11-21 13:51:01
    网络上有太多关于JAVA参数传递是传值还是传引用的讨论,其实大多是概念不清,混淆视听。从程序运行的角度来看,参数传递,只有传值,从不传递其它的东西。只不过值的内容有可能是数据,也有可能是一个内存地址。  ...

    网络上有太多关于JAVA参数传递是传值还是传引用的讨论,其实大多是概念不清,混淆视听。从程序运行的角度来看,参数传递,只有传值,从不传递其它的东西。只不过值的内容有可能是数据,也有可能是一个内存地址。

      Java中的数据类型有两大类,即基本类型(primitive types), 共有8种,包括int, short, long, byte, float, double, boolean, char,存在于栈(Stack)中。另一种暂称为对象类型,包括Integer, String, Double等相应基本数据类型的包装类,以及其他所有JAVA自带和用户自定义的类,这些类数据全部存在于堆中(Heap),如下图所示。

       对象类型的数据不同于基本类型的数据,我们所定义的对象变量并不是对象实例本身,而是对象的一个引用(reference)地址,该内存地址用来定位该对象实例在HEAP中的位置。对象实例本身和对象的引用分别保存在HEAP和STACK中,对象引用和对象实例之间的关系好比遥控器和电视机之间的关系,在房间走动的时候里,你只需拿着遥控器就可以控制电视机,而不必带着电视机。而且,即使没有电视机,遥控器也可以独立存在,也就是说你可以定义一个对象类型的变量,但是可以暂时不和一个对象实例关联。多个对象引用也可以指向同一个对象实例。

     

               Heap(堆)       Stack(栈)
     JVM中的功能            内存数据区                    内存指令区
     存储数据  对象实例  基本数据类型, 指令代码,常量,对象的引用地址

     

    下面我们来看看几个例子,您就会更加明白。

    例子1:

    public class Test {
    public static void changeValue(int i) {
    i=2;
    System.out.println("during test: i = " + i);
    }
    public static void main(String[] args) {
    int i = 1;
    System.out.println("Before test: i = " + i);
    changeValue(i);
    System.out.println("After test: i = " + i);
    }
    }
      运行结果:
    Before test: i = 1
    during test: i = 2
    After test: i = 1

    不难看出,虽然在 changeValue(int i)方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响。其内部原理是,main方法里的变量和changeValue方法的参数是两个不同的变量,以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法的,那么在方法里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。

    例子2:

    public class Test {
    public static void test(StringBuffer str) {
    str.append(", World!");
    }
    public static void main(String[] args) {
    StringBuffer string = new StringBuffer("Hello");
    test(string);
    System.out.println(string);
    }
    }
      运行结果:
    Hello, World!

       在这个例子里,似乎变量string被“改变”了。但其实改变的并不是string变量本身,也就是说string保存的内存地址并没有被改变,改变的是它所指向的对象实例。内部原理是这样的,在main方法里定义了一个对象引用string,并且把它和一个对象实例关联new StringBuffer。方法调用的时候,string所保存的对象实例的内存地址传递给了test方法的对象引用参数str,这时就有两个对象引用变量指向同一个对象实例。这两个对象引用都可以对该对象实例进行操作,操作结果都有效,因此在test方法执行完之后,对象实例的内容已经被改变了,这个时候再通过main方法里的string引用去查看对象实例的内容,看到的就是改变之后的内容。

    例子3:

    public class Test {
    public static void test(String str) {
    str = "World";
    }
    public static void main(String[] args) {
    String string = "Hello";
    test(string);
    System.out.println(string);
    }
    }

    运行结果:

    Hello。

    这个结果和上面结果矛盾吗?一点也不矛盾。在这个例子中,参数传递过程和上个例子完全一样,但是在test方法里并不是对原来指向的对象实例进行操作,而是把str指向了另外一个对象实例,当然对原来的对象实例毫无影响。   


    总结:对于例1(基本数据类型),大家应该比较容易理解,因为Java调用方法的参数传递方式为“传值”,如method(O o),Java调用method(a),a传给o,即o的值等于a,但是明确一点,o与a是分割且独立的,可以理解o为a的一份拷贝,那么在方法method里面对o进行的任何操作,不会对a产生影响。就像电脑E盘下有一份word文档E:/B.doc,现在你要对B里面的内容进行修改,但是B.doc同样也是要使用的,那么就拷贝一份B.doc到F盘下名为F:/C.doc,那么B.doc和C.doc里面的内容完全相同,只是位置不同,B.doc在E盘下,C.doc在F盘下,他俩是相互分割且独立的,操作互不影响;

    对于例2和例3(对象/引用数据类型),其实和例1的原理是一样的,只是有点绕。这里要求大家回顾并理解Java中“对象”的概念和“如何操纵对象”,Java编程思想(第四版)第二章(一切都是对象)2.1节(用引用操纵对象)中第一段第一句话:“每种编程语言都有自己的操纵内存中元素的方式。”这句话貌似没有提到对象,但是提到了“内存中元素”,不管是基本数据类型还是对象/引用数据类型,都是数据,数据在Java虚拟机里都是分配在内存中(关于数据如何分配,请看《深入理解Java虚拟机》一书),所以这句话中”内存中元素“我们可以理解为”对象“。第二段第三句话:”尽管一切都看作对象,但操纵的标识符实际上是对象的一个“引用”(reference)。”从这两句话,我们可以这样理解:Java中的对象(数据)都保存在内存中,但是其实是通过直接操纵对象的一个引用来间接操纵对象。这里有个关键词“引用”,下面这张图片是Java编程思想(第四版)中对“引用”一词的解释,长篇大论。


    我先不理会书中的解释,给出自己形象的理解,可以把“引用”理解为电脑中的“快捷方式”,C盘深层目录下有一份word文档名为C:/Object/test/B.doc,但是我想用它,每次都要点开“我的电脑”-->C盘,然后再点到B.doc所在的目录,感觉狠繁琐,就把它生成一个快捷方式到桌面,右键快捷方式-->属性-->快捷方式栏目中有“目标”:C:/Object/test/B.doc这一项,这个目标就是说B.doc文件在这个快捷方式指向的位置C:/Object/test/B.doc,以后就可以直接操作快捷方式来间接操作B.doc了,是不是狠方便!

    上面对“对象”、“如何操纵对象”和“引用”做了一个详细的解释,现在我们言归正传,仍然以method(O o)为例,Java调用method(A),A为对象引用(快捷方式),对象实例(目标地址)为***,A传给o,o也是快捷方式,这里传的到底是什么?疑问我肯定地告诉你,是“值”,是对象实例的地址。那么对于例2,是不是就狠好理解了?是不是狠嗨皮!大笑但是这才是开始,例2和例3看着不是一样的么,为何结果不一样,你是不是又想哭了?快哭了注意例2中test方法体中操作对象的是这句:str.append(", World!");例3中的是:str = "World";结果不一样的关键就在于这两句话,现在我们应该都知道str是String对象“Hello”的快捷方式了吧!由“点”操作符的意义可知例2中str.append(", World!");中的append方法是String对象自身的方法,可以理解为word文档的“编辑”功能,可知是对str自身进行操作,也可以理解为对word文档内容进行修改;而例3中str = "World";的“=”操作符是赋值操作符,如果你看了《Java编程思想(第四版)》中第三章(操作符)3.4节(赋值)的内容,第一段第一句话是:“赋值使用操作符”=“。它的意思是‘取右边的值(即右值),把它复制给左边(即左值)’”这是对操作符的理解,但是这后面还有一段话,如下图:



    你是不是又觉得例3错了呢?纠结不抓狂,其实,上图对赋值操作符操纵对象的解释是完全正确的,而例3并没有违背以上解释,只是Java自带的特殊类String,关于String类的一系列特点和在Java中的特殊之处,在此就不赘述了,我只说结果,new操作符大家应该不陌生了吧,《Java编程思想(第四版)》中第二章(一切都是对象)2.2节(必须由你创建对象)第一段第一句:“一旦创建了一个引用,就希望它能与一个新的对象相关联。通常用new操作符来实现这一目的。new关键字的意思是‘给我一个新对象。’”这句话中“给我一个新对象”表明new操作符给引用一个新的内存地址值,而String str =“****”等同于String str = new String(“****”),例3中str = "World";意思就是说将新对象“World”的内存地址和str引用相关联,即例3中的快捷方式str指向新对象“World”的内存地址(目标位置),而例3中最后打印的是快捷方式string仍然指向原来的对象“Hello”,最后打印结果当然是“Hello”。到这里,我想例3的结果大家也认为是对的了吧!是不是又兴奋了一把大笑,终于把所有的问题都给Over了。

    总结之前的内容是拷贝的,总结之后是本菜鸟写的,其实看似简单,但是涵盖了很多对Java基础知识的理解!写得不好,勿喷~

    展开全文
  • ViewGroup和View的事件传递原理

    千次阅读 2015-11-08 20:56:22
    View和ViewGroup的事件传递机制 view和viewgroup的事件传递机制。很早就想好好改清楚里面的关系。终于花了一个星期的时间,一直研究,基本上可以解决我们常遇到问题。本文以例子和源码为主

    View和ViewGroup的事件传递机制

    view的事件传递一直是我一个软肋,以前我知道个大概,就是表面的意思是这样的,但是具体到这一些细节还是出了问题,所以,我花了较多时间看了下源码,并从其他的人的博客得到一些启发,但是还多博客都是一上来就是源码的,给人一种慢慢看完了不知道在说什么,只有等到自己梳理一遍,才完全知道。
    本文要说明view的事件传递机制,从源码入手,清晰说明MotionEvent在view和activity的中传递的过程,虽然源码较多,但是我采取一种主线下去的感觉:

    另外欢迎大家指出其中错误

    • TouchTarget类的简单说明

    • Activity和View之间的motionEvent的传递

    • ViewGroup的关于MotionEvent传递方法的介绍

    • View的关于MotionEvent传递方法的介绍

    • 总结

    • 感谢


    TouchTarget的介绍

    touchtarget是viewgroup的一个静态类,在这面我们贴下的它的源码

    private static final class TouchTarget {
            private static final int MAX_RECYCLED = 32;
            private static final Object sRecycleLock = new Object[0];
            private static TouchTarget sRecycleBin;
            private static int sRecycledCount;
    
            public static final int ALL_POINTER_IDS = -1; // all ones
    
            // The touched child view.
            public View child;
    
            // The combined bit mask of pointer ids for all pointers captured by the target.
            public int pointerIdBits;
    
            // The next target in the target list.
            public TouchTarget next;
    
            private TouchTarget() {
            }
    
            public static TouchTarget obtain(View child, int pointerIdBits) {
                final TouchTarget target;
                synchronized (sRecycleLock) {
                    if (sRecycleBin == null) {
                        target = new TouchTarget();
                    } else {
                        target = sRecycleBin;
                        sRecycleBin = target.next;
                         sRecycledCount--;
                        target.next = null;
                    }
                }
                target.child = child;
                target.pointerIdBits = pointerIdBits;
                return target;
            }
    
            public void recycle() {
                synchronized (sRecycleLock) {
                    if (sRecycledCount < MAX_RECYCLED) {
                        next = sRecycleBin;
                        sRecycleBin = this;
                        sRecycledCount += 1;
                    } else {
                        next = null;
                    }
                    child = null;
                }
            }
        }
    

    不难发现TouchTarget是一个链表结构的final类,其中sRecycleBin保存着当前的所有touchtarget的对象(static),另外next的中包含下一个信息。并且通过sRecycledCount来计数。
    接下来我们简单说下其中obtain()和recycle()的方法。
    obtain()

    target = sRecycleBin;
    sRecycleBin = target.next;
    sRecycledCount--;
    target.next = null;

    新生成的target 的中next指向以前的以前存在的对象,就是依次往回加的感觉,同时sRecycledCount是减一的。

    接下来就是recycle()的

    next = sRecycleBin;
    sRecycleBin = this;
    sRecycledCount += 1;

    就是把链表中最上面的去掉,也就是最新obtain()的那个给去掉。同样也是谁调用recycle()的这个对象在链表被去除了。但是不影响链表其他的对象,这点非常重要。
    TouchTarget类暂时说到这儿了。

    Activity和View之间的motionEvent的传递

    我们知道activity显示的view是通过window来显示,同样在事件的传递的过程中,activity的motionevent也是通过window去传递给view的,看下面的源码你就知道了
    activity中dispatchTouchEvent()

    1 public boolean dispatchTouchEvent(MotionEvent ev) {
    2        if (ev.getAction() == MotionEvent.ACTION_DOWN)  {
    3            onUserInteraction();
    4        }
    5        if (getWindow().superDispatchTouchEvent(ev)) {
    6            return true;
    7        }
    8        return onTouchEvent(ev);
    9    }

    注意第6行中getWindow().superDispatchTouchEvent(ev)这行代码的意思,给中getWindow即为phoneWindow这个类
    phoneWindow中的superDispatchTouchEvent的方法

    public boolean superDispatchTouchEvent(MotionEvent event) {  
            return mDecor.superDispatcTouchEvent(event);  
    }

    mDecor的其实就是activity的根view,这个时候也就将MotionEvent从activity传到了view中。
    从上面我们可以看出了,activity对MotionEvent的传递先给交给mDecor(ViewGroup)进行分发,如果分发不成功,则交给自己的自己的onTouch()方法。这个结论先记住。

    ViewGroup的关于MotionEvent传递

    首先在说传递之前,我给大家说明两个词的概念
    1 处理:MotionEvent交给它来处理,并不代表它能消费掉MotionEvent。
    2 消费:MotionEvent不仅交给它处理,同样的也消费了这个MotionEvent。

    明白上面两个词的概念后。我们先说明下,ViewGroup的关于MotionEvent传递的方法有些:
    1 dispatchTouchEvent
    2 onInterceptTouchEvent
    3 requestDisallowInterceptTouchEvent
    4 onTouchEvent
    借助一段伪代码先概括下的ViewGroup关于MotionEvent事件传递

    public boolean dispatchTouchEvent(MotionEvent event){
        boolean handle = false;
        if(!onInterceptTouchEvent(event) || requestDisallowInterceptTouchEvent(event)&& event.getActionCode != MotionEvent.ACTION_DOWN){
        handle = child.dispatchEvent();
        }
        if(!handle){
            handle = onTouchEvent();
        }
        return handle;
    }

    这点我们先验证一下:首先我们先定义一个NoDispatchTouchEventViewGroup和另一个TouchEventViewGroup的两个viewgroup
    NoDispatchTouchEventViewGroup的中的dispatchTouchEvent的方法如下dispatchTouchEvent中让直接返回false这里写代码片

    @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.e("TAG", "NoDispatchTouchEvent   dispatchTouchEvent ");
            return false;
        }

    TouchEventViewGroup dispatchTouchEvent的方法我们不做处理,但是我们在onTouchEvent()中让他消费这个事件

     @Override
       public boolean onTouchEvent(MotionEvent event) {
            Log.e("TAG", "TouchEventViewGroup 的onTouchEvent");
            return true;
        }

    activity的layout文件如下,就是让TouchEventViewGroup里面放着NoDispatchTouchEventViewGroup

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <com.idreamo.rrtoyewx.rrtoyewx.view.TouchEventViewGroup
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <com.idreamo.rrtoyewx.rrtoyewx.view.NoDispatchTouchEventViewGroup
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
            </com.idreamo.rrtoyewx.rrtoyewx.view.NoDispatchTouchEventViewGroup>
    
    
        </com.idreamo.rrtoyewx.rrtoyewx.view.TouchEventViewGroup>
    
    
    </RelativeLayout>

    这个时候打印的结果如下
    这里写图片描述
    可以看到运行了一次NoDispatchTouchEventViewGroup的中的dispathTouchEvent(),然后以后就直接交给了TouchEventViewGroup的onTouchEvent()的方法。这也就是上面所说的处理和消费,可以看到,NoDispatchTouchEventViewGroup处理过这个事件的中down事件(down 是一个系列事件的最开始),但是并没有消费掉,这也就是后来他接受不到这个系列事件后来的事件的原因。而这系列的处理过程是在viewgroup的dispatchTouchEvent()中实现。也就是意味着viewgroup的dispatchTouchEvent()决定了事件是否向下传递。
    正如上面的我们伪代码一样。但是此时我们改下上面这个代码。就是在NoDispatchTouchEventViewGroup的dispatchTouchEvent()的返回值改为true;打印的log的为
    这里写图片描述
    并不没有打印NoDispatchTouchEventViewGroup的ontouchevent()
    这也就意味了viewgroup的dispatchTouchEvent()的控制着自身的onTouchEvent()的处理过程,dispatchTouchEvent()不仅管控着如何处理这个事件(传递),同时也管控着如消费这个事件。
    到这里是不是觉得viewgroup的dispatchTouchEvent()方法很神奇,好的,正如所愿,接下来我们来看下dispatchTouchEvent()的源码:

       public boolean dispatchTouchEvent(MotionEvent ev) {
    1      if (mInputEventConsistencyVerifier != null) {
    2            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
            }
    
            // If the event targets the accessibility focused view and this is it, start
            // normal event dispatch. Maybe a descendant is what will handle the click.
    3        if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
    4           ev.setTargetAccessibilityFocus(false);
            }
    
    5        boolean handled = false;
    6        if (onFilterTouchEventForSecurity(ev)) {
    7            final int action = ev.getAction();
    8            final int actionMasked = action & MotionEvent.ACTION_MASK;
    
                // Handle an initial down.
    9           if (actionMasked == MotionEvent.ACTION_DOWN) {
                    // Throw away all previous state when starting a new touch gesture.
                    // The framework may have dropped the up or cancel event for the previous gesture
                    // due to an app switch, ANR, or some other state change.
    10              cancelAndClearTouchTargets(ev);
    11               resetTouchState();
    12           }
    
                // Check for interception.
    13            final boolean intercepted;
    14            if (actionMasked == MotionEvent.ACTION_DOWN
                        || mFirstTouchTarget != null) {
    15               final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    16                if (!disallowIntercept) {
    17                    intercepted = onInterceptTouchEvent(ev);
    18                    ev.setAction(action); // restore action in case it was changed
    19                } else {
    20                    intercepted = false;
    21                }
    22            } else {
                    // There are no touch targets and this action is not an initial down
                    // so this view group continues to intercept touches.
    23                intercepted = true;
    24            }
    
                // If intercepted, start normal event dispatch. Also if there is already
                // a view that is handling the gesture, do normal event dispatch.
    25            if (intercepted || mFirstTouchTarget != null) {
    26                ev.setTargetAccessibilityFocus(false);
    27           }
    
                // Check for cancelation.
    
    28            final boolean canceled = resetCancelNextUpFlag(this)
                        || actionMasked == MotionEvent.ACTION_CANCEL;
    
                // Update list of touch targets for pointer down, if needed.
    29            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
    30            TouchTarget newTouchTarget = null;
    31            boolean alreadyDispatchedToNewTouchTarget = false;
    32           if (!canceled && !intercepted) {
    
                    // If the event is targeting accessiiblity focus we give it to the
                    // view that has accessibility focus and if it does not handle it
                    // we clear the flag and dispatch the event to all children as usual.
                    // We are looking up the accessibility focused host to avoid keeping
                    // state since these events are very rare.
    33               View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                            ? findChildWithAccessibilityFocus() : null;
    
    34                if (actionMasked == MotionEvent.ACTION_DOWN
                            || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                            || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
    35                    final int actionIndex = ev.getActionIndex(); // always 0 for down
    36                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                                : TouchTarget.ALL_POINTER_IDS;
    
    37 removePointersFromTouchTargets(idBitsToAssign);
    
    38                    final int childrenCount = mChildrenCount;
    39                    if (newTouchTarget == null && childrenCount != 0) {
    40                        final float x = ev.getX(actionIndex);
    41                        final float y = ev.getY(actionIndex);
    
    42                       final ArrayList<View> preorderedList = buildOrderedChildList();
    43                        final boolean customOrder = preorderedList == null
                                    && isChildrenDrawingOrderEnabled();
    44                       final View[] children = mChildren;
    45                       for (int i = childrenCount - 1; i >= 0; i--) {
    46                            final int childIndex = customOrder
                                        ? getChildDrawingOrder(childrenCount, i) : i;
    47                            final View child = (preorderedList == null)
                                        ? children[childIndex] : preorderedList.get(childIndex);
    
                                // If there is a view that has accessibility focus we want it
                                // to get the event first and if not handled we will perform a
                                // normal dispatch. We may do a double iteration but this is
                                // safer given the timeframe.
    48                            if (childWithAccessibilityFocus != null) {
    49                               if (childWithAccessibilityFocus != child) {
    50                                    continue;
    51                                }
    52                                childWithAccessibilityFocus = null;
    53                                i = childrenCount - 1;
    54                            }
    
    55                            if (!canViewReceivePointerEvents(child)
                                        || !isTransformedTouchPointInView(x, y, child, null)) {
    56                                ev.setTargetAccessibilityFocus(false);
    57                                continue;
    58                            }
    
    59                            newTouchTarget = getTouchTarget(child);
    60                            if (newTouchTarget != null) {
                                    // Child is already receiving touch within its bounds.
                                    // Give it the new pointer in addition to the ones it is handling.
    61                                newTouchTarget.pointerIdBits |= idBitsToAssign;
    62                                break;
    63                            }
    
    64                            resetCancelNextUpFlag(child);
    65                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                    // Child wants to receive touch within its bounds.
    66                                mLastTouchDownTime = ev.getDownTime();
    67                                if (preorderedList != null) {
                                        // childIndex points into presorted list, find original index
    68                                    for (int j = 0; j < childrenCount; j++) {
    69                                        if (children[childIndex] == mChildren[j]) {
    70                                            mLastTouchDownIndex = j;
    71                                            break;
    72                                        }
    73                                    }
    74                                } else {
    75                                    mLastTouchDownIndex = childIndex;
    76                                }
    77                                mLastTouchDownX = ev.getX();
    78                                mLastTouchDownY = ev.getY();
    79                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
    80                                alreadyDispatchedToNewTouchTarget = true;
    81                                break;
    82                            }
    
                                // The accessibility focus didn't handle the event, so clear
                                // the flag and do a normal dispatch to all children.
    83                            ev.setTargetAccessibilityFocus(false);
    84                        }
    85                        if (preorderedList != null) preorderedList.clear();
    86                    }
    
    87                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                            // Did not find a child to receive the event.
                            // Assign the pointer to the least recently added target.
    88                        newTouchTarget = mFirstTouchTarget;
    89                        while (newTouchTarget.next != null) {
    90                            newTouchTarget = newTouchTarget.next;
    91                        }
    92                        newTouchTarget.pointerIdBits |= idBitsToAssign;
    93                    }
    94                }
    95            }
    
                // Dispatch to touch targets.
    96            if (mFirstTouchTarget == null) {
                    // No touch targets so treat this as an ordinary view.
    97                handled = dispatchTransformedTouchEvent(ev, canceled, null,
    98                        TouchTarget.ALL_POINTER_IDS);
    99            } else {
                    // Dispatch to touch targets, excluding the new touch target if we already
                    // dispatched to it.  Cancel touch targets if necessary.
    100                TouchTarget predecessor = null;
    101                TouchTarget target = mFirstTouchTarget;
    102                while (target != null) {
    103                    final TouchTarget next = target.next;
    104                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
    105                        handled = true;
    106                    } else {
    107                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
    108                                || intercepted;
    109                        if (dispatchTransformedTouchEvent(ev, cancelChild,
    110                                target.child, target.pointerIdBits)) {
    111                            handled = true;
    112                        }
    113                        if (cancelChild) {
    114                            if (predecessor == null) {
    115                                mFirstTouchTarget = next;
    116                            } else {
    117                                predecessor.next = next;
    118                            }
    119                            target.recycle();
    120                            target = next;
    121                            continue;
    122                        }
    123                    }
    124                    predecessor = target;
    125                    target = next;
    126                }
    127            }
    
                // Update list of touch targets for pointer up or cancel, if needed.
    128            if (canceled
                        || actionMasked == MotionEvent.ACTION_UP
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
    129                resetTouchState();
    130            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
    131                final int actionIndex = ev.getActionIndex();
    132                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
    133                removePointersFromTouchTargets(idBitsToRemove);
    134            }
    135        }
    
    136        if (!handled && mInputEventConsistencyVerifier != null) {
    137            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    138        }
    139        return handled;
    140    }

    是不是后悔了,一开始的兴奋被这140多行代码彻底浇灭了。没事,其实我也不是很明白每一行的代码的意思。但是我采用这个最简单教大家来看,我们都知道一个系列的事件是由一个down事件和若干个move事件和一个up事件组成的,所以在这里我们就按照这个思路来上看上面的代码。

    先看down事件,过滤下上面的代码
    首先是9到12行

    if (actionMasked == MotionEvent.ACTION_DOWN) {  
        cancelAndClearTouchTargets(ev);
        resetTouchState();
       }

    cancelAndClearTouchTargets(ev)中有个clearTouchTargets()的方法,看一下,我们会发现其中调用recycle()的方法,正如我门上面所说的mFirstTouchTarget将会从sRecycleBin中移除,另外
    cancelAndClearTouchTargets的方法中同样将child也从sRecycleBin给移除了

    private void clearTouchTargets() {
            TouchTarget target = mFirstTouchTarget;
            if (target != null) {
                do {
                    TouchTarget next = target.next;
                    target.recycle();
                    target = next;
                } while (target != null);
                mFirstTouchTarget = null;
            }
        }

    resetTouchState()

     private void resetTouchState() {
       clearTouchTargets();
       resetCancelNextUpFlag(this);
       mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
       mNestedScrollAxes = SCROLL_AXIS_NONE;
     }

    这里将mGroupFlags给重置了。这点我们只需要关注着么多,总之9到12行的代码主要将viewgroup的在down 之前进行状态重置和初始化。
    接下来12到21行主要说明了拦截的处理,但是呢,我们是down 的事件所以进入if内部,但是此时呢我们应该知道mFirstTouchTarget = null的,进入先判断disallowIntercept()的这个值,这个值主要是通过FLAG_DISALLOW_INTERCEPT的这个标志来决定的,而这个标志是通过requestDisallowInterceptTouchEvent来决定的

     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    
            if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
                // We're already in this state, assume our ancestors are too
                return;
            }
    
            if (disallowIntercept) {
                mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
            } else {
                mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
            }
    
            // Pass it up to our parent
            if (mParent != null) {
                mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
            }
        }

    通过child调用getParent().requestDisallowInterceptTouchEvent的方法让这个标志位改变,并且将一致延续到最上层viewgroup。
    如果child要求这个事件,此时会让onInterceptTouchEvent的返回值实效,但是这个前提是motionevent的actionCode为down,或者mFirstTouchTarget!=null的时候,而mFirstTouchTarget!=null是在down事件传递下去才会有值(这一点下面可以知道)所以我们记住第一个结论,
    通过child调用getParent().requestDisallowInterceptTouchEvent()的可以屏蔽掉父亲的onInterceptTouchEvent拦截的返回值,但是如果需要在这个系列的事件都有效,则需要将这个系列的事件down传递到孩子,即父亲不拦截down事件的向下传递,当然父亲的onInterceptTouchEvent的这个默认返回值为false。

    public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }
    

    好了 ,接下里我们先默认这个down事件不拦截向下分发接下来进入33到94行中代码,在进入判断之前,不仅时MotionEvent.ACTION_DOWN,还有split && actionMasked == MotionEvent.ACTION_POINTER_DOWN,前面是单指,后面是有的新的手指加入,在这我们先考虑MotionEvent.ACTION_DOWN的情况下。同时更多关于MotionEvent的知识可以看下我之前写的博客android MotionEvent的相关的类的介绍

    接下来进入for循环中对每一个孩子进行遍历,按照从后向前的顺序检查这个MotionEvent是不是在这个child上(canViewReceivePointerEvents()和isTransformedTouchPointInView()),并交给他去处理(dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)),接下来我们看下dispatchTransformedTouchEvent()部分的源码

           if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
                event.setAction(MotionEvent.ACTION_CANCEL);
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    handled = child.dispatchTouchEvent(event);
                }
                event.setAction(oldAction);
                return handled;
            }

    故此时,将事件分发给了孩子的dispatchTouchEvent()中,完成了down的事件的分发。并且当child的dispatchTouchEvent()的返回值为true的时候,这个时候将mFirstTouchTarget给赋值了。

     newTouchTarget = addTouchTarget(child, idBitsToAssign);
     alreadyDispatchedToNewTouchTarget = true;

    看下addTouchTarget的源码,其中

        private TouchTarget addTouchTarget(View child, int pointerIdBits) {
            TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
            target.next = mFirstTouchTarget;
            mFirstTouchTarget = target;
            return target;
        }

    如果此时viewgroup不是mDecorView,则我们知道TouchTarget的sRecycleBin其实是有值的(static),所以到现在你是不是觉得TouchTarget的在事件传递过程中扮演是什么角色了,它其实就是记录着事件传递过程中成功消费这个事件的所有的view和他的父亲,mFirstTouchTarget最上面的那个TouchTarget对象记录着事件被消费的childview的信息,通过链表来联系。接下来在这段代码中还有87到94 ,这段代码时处理多指的代码,当然这里不做多分析。当然这里主要说明了down的事件被孩子dispatchTouchEvent()返回true的时候,那么如果孩子返回false呢?接下向下看96到98 ,我们可以看到mFirstTouchTarget= null的时候,即这个down事件没有被孩子dispatchTouchEvent()的返回true的时候。回去调用handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
    dispatchTransformedTouchEvent的源码我们也贴出来,我们看到此时去调用了view.dispatchToucEvent(),这里我简单说下view.dispatchTouchEvent()简单源码,首先判断这个控件有没有onTouchListener,如果是则直接return true 并且调用相应的回调方法,否则,则调用本身的onTouchEvent()的方法,所以这里我们是viewgroup默认是没有onTouchListener,所以这个时候会去调用onTouchEvent(),根据其中的返回值来说明是否被down事件是否被消费了。最后将handle给返回。
    好了,上面上基本将down的在viewgroup的dispatchTouchEvent()如何传递梳理一下,接下来我们看下move和up,其实move和up差不多的。
    move的时候,如果mFirstTouchTarget!=null,是可以进入到是否判断拦截的。
    mFirstTouchTarget!=null,如果不是不拦截的,直接来到106行,调用了while循环去遍历孩子,判断谁之前消费了他,紧接再叫他处理dispatchTransformedTouchEvent(event),但是如果拦截呢?此时有113—120代码,可以看出来此时mFirstTouchTarget=null了,但是,此时谁去处理这个事件呢?理论上是交给activity的onTouchEvent()去处理,但是如果在消费这个事件的父亲上面有的可能针对这个事件去消费的话,则会交它去消费的( 特殊情况下,需要手动实现,不做考虑)。这个系列的事件此后的事件都默认不会交给这个child去处理了。有种感觉就是你要做,就得从头到尾都处理好这件事,如果其中有个事件你没做好,对不起,以后你都没机会了,不管你前面处理多好多好。

    接下来我们来看个例子。例子1
    一方面来验证我们前面所说的,另一方面来加深我们对之前的理解。我们自定义两个view ,一个ViewGroup,和一个View,代码如下:
    TouchView extends View

     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchView dispatchTouchEvent ,actionCode : "+actionCode);
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            int actionCode = event.getAction();
            Log.e("TAG","TouchView  onTouchEvent"+", actionCode :" +actionCode);
            return true;
        }

    TouchViewGroup extends ViewGroup

     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()& MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup dispatchTouchEvent   ,actionCode"+actionCode);
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int actionCode = event.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG","TouchViewGroup  onTouchEvent"+", actionCode :" +actionCode);
            return true;
    
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            return super.onInterceptTouchEvent(ev);
        }

    另外我们在MainActivity重写了其中的onTouchEvent

    @Override
        public boolean onTouchEvent(MotionEvent event) {
            int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
            Log.e("TAG","MainActivity onTouchEvent   actionCode :" + actionCode);
            return super.onTouchEvent(event);
        }

    最后再贴下布局文件

     <com.example.rrtoyewx.person.TouchViewGroup
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white">
            <com.example.rrtoyewx.person.TouchView
                android:layout_width="match_parent"
                android:layout_height="match_parent">
            </com.example.rrtoyewx.person.TouchView>
    </com.example.rrtoyewx.person.TouchViewGroup>

    这是最简单的事件传递机制,我们在最上层的TouchView的onTouchEvent(),返回值为true,成功消费这个系列的事件。接下来我们log的打印的情况
    这里写图片描述
    可以看到,正如我们上面分析的一样的,当事件传递过来的时候,整体传递的过程为 TouchViewGroup.dispatchTouchEvent—->TouchViewGroup.onInterceptTouchEvent——>TouchView.dispatchTouchEvent—>TouchView.onTouchEvent.这也是最简单的事件传递。
    接下来我们就来修改下代码
    例子2 第一,我们在TouchViewGroup的onInterceptTouchEvent的返回值改为true;因为默认是false的,修改如下

    @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            return true;
        }

    此时应该正如我们上面所分析的一样,最终会交给了activity的onTouchEvent(),因为down的时候,把事件交给上面的view的处理时,TouchView没有接受到事件,就交给了TouchViewGroup,TouchViewGroup虽然处理了事件,但是并没有消费事件,所以TouchViewGroup的中mFirstTouchTarget并没有赋值。看下log是不是正如我们所说的
    这里写图片描述
    看了log 确实正如我们所说的,
    例子3,前面这两个似乎都挺容易理解的,接下来我们慢慢加深点,看大家能不能理解了,我们在TouchViewGroup的onInterceptTouchEvent的方法中,对down事件不进行拦截,我们对第一个move事件也不拦截,但是呢,我们对之后的事件开始拦截。代码修改如下:

    public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    return false;
                case MotionEvent.ACTION_MOVE:
                    if(mFirst){
                        mFirst = false;
                        return false;
                    }else{
                        return true;
                    }
                default:
                    mFirst = true;
                    return true;
            }
        }

    在看log之前,我们先分析下,down事件是可以传递下去,而且被消费了,mFirstTouchTarget被赋值了,此时当一个move事件来,也是可以被消费了,但是第二个move事件的时候,在判断是否拦截了,这个时候这个时间就没办了消费了,并且从之前的代码说明了此时mFirstTouchTarget = null;所以接下来事件都不会交到里面的那个TouchView了,全部会交给谁上次消费了这个事件,但是它的上次层的TouchViewGroup依然可以处理到这个事件,不像例子2,不会直接交给了activity里面,这点尤为重要
    这里写图片描述
    似乎log正如我们上面所分析的,第一个down事件成功被消费后,第二个事件来了似乎和我们上面所说的不太一样,并且你发现此时的actioncode变成3,也就意味着这是一个cancle的事件,其实这点我也没找到相应的转化代码,但是我的理解的是,viewgroup的里面的部分源码

    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                    || intercepted;
                            if (dispatchTransformedTouchEvent(ev, cancelChild,
                                    target.child, target.pointerIdBits)) {
                                handled = true;
                            }
                            if (cancelChild) {
                            }

    可以看到先交给他去处理,接下来拦截,所以这个事件是无效。希望懂的童鞋的能在下面的留言给我答案。我真的很想知道了。。。。
    但是这个无关大碍。接下来的log证实我们上面所说的,以后的事件他都会交给TouchViewGroup(TouchView的上面的view)的去处理,尽管它并没有消费掉,因为此时TouchViewGroup的mFirstTouchTarget==null,但是TouchViewGroup他上层的mFirstTouchTarget是有值的,并指向TouchViewGroup的。所以每次都会传到这儿的,不会像例子2种所有的viewGroup的mFirstTouchTarget都是==null的。这点希望大家慢慢体会下。
    这个时候,我们就可以做些事情了,比如TouchViewGroup拦截了事件,可以针对其中的某一些事件做些消费。代码实现也很简单。只需要我们我TouchViewGroup里面的onTouchEvent()做响应处理就可以了。
    例子4 ,我们似乎忘了一个requestDisallowInterceptTouchEvent方法了。接下来我们再改下代码
    TouchViewGroup的代码

     public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()& MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup dispatchTouchEvent   ,actionCode"+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_MOVE:
                    requestDisallowInterceptTouchEvent(true);
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int actionCode = event.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG","TouchViewGroup  onTouchEvent"+", actionCode :" +actionCode);
            return super.onTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    return true;
                case MotionEvent.ACTION_MOVE:
                    return true;
                default:
                    return true;
            }
        }

    大家可以猜下log的打印情况(其实这是一种拦截的错误的写法),至于为什么会出现下面的log,我上面已经说出来,
    这里写图片描述
    例子5 ,正确的写法
    TouchViewGroup的代码

    public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()& MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup dispatchTouchEvent   ,actionCode"+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_MOVE:
                    requestDisallowInterceptTouchEvent(true);
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int actionCode = event.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG","TouchViewGroup  onTouchEvent"+", actionCode :" +actionCode);
            return super.onTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    return false;
                case MotionEvent.ACTION_MOVE:
                    return true;
                default:
                    return true;
            }
        }

    我上面说了,要想用requestDisallowInterceptTouchEvent的有效。必须在TouchViewGroup的不拦截down事件。我们看下log
    这里写图片描述
    成功的反拦截了。
    例子6 ,其实这是我以前犯了一错误,其实那样是拦截不到,我贴下代码,大家可以看下,如果你看懂了,那么基本上ViewGroup的事件传递基本上都看懂了。
    TouchViewGroup的代码

    public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()& MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup dispatchTouchEvent   ,actionCode"+actionCode);
    
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int actionCode = event.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG","TouchViewGroup  onTouchEvent"+", actionCode :" +actionCode);
            return super.onTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    return false;
                case MotionEvent.ACTION_MOVE:
                    return true;
                default:
                    return true;
            }
        }

    什么都没做,就是拦截move事件
    接下来就是TouchView的代码

    @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction() & MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchView dispatchTouchEvent ,actionCode : " + actionCode);
            switch (actionCode) {
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_MOVE:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            int actionCode = event.getAction();
            Log.e("TAG", "TouchView  onTouchEvent" + ", actionCode :" + actionCode);
    
            return true;
        }

    或者是这样

    @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction() & MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchView dispatchTouchEvent ,actionCode : " + actionCode);
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            int actionCode = event.getAction();
            Log.e("TAG", "TouchView  onTouchEvent" + ", actionCode :" + actionCode);
            switch (actionCode) {
                case MotionEvent.ACTION_DOWN:
                    break;
                case MotionEvent.ACTION_MOVE:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
            }
            return true;
        }

    打印的结果都是这样子的
    这里写图片描述
    结果却是没拦截到。是不是觉得很奇怪,不急,我在改点东西,我让第一move事件不拦截,其他都拦截,就像我们上面写的代码一样
    TouchViewGroup的代码

      public boolean onInterceptTouchEvent(MotionEvent ev) {
            int actionCode = ev.getAction()&MotionEvent.ACTION_MASK;
            Log.e("TAG", "TouchViewGroup onInterceptTouchEvent  ,actionCode: "+actionCode);
            switch (actionCode){
                case MotionEvent.ACTION_DOWN:
                    return false;
                case MotionEvent.ACTION_MOVE:
                    if(mFirst){
                        mFirst = false;
                        return false;
                    }else{
                        return true;
                    }
                default:
                    mFirst = true;
                    return true;
            }
        }

    此时log,你会惊讶的,因为此时居然可以反拦截了这里写图片描述
    到这儿,似乎你有点纳闷了,为什么跳过了一个down的事件的就可以拦截了,其实是顺序的问题。你要想想,执行的顺序是什么?先执行TouchViewGroup dispatchTouchEvent ,这个时候已经在判断FLAG_DISALLOW_INTERCEPT的标志位,并没有改变,直到你在TouchView的dispatchTouchEvent去改变,这个时候,已经是然并卵了。最好就是在TouchView的down的时候就开始反拦截。然后根据需求来在决定在以后需不需要不反拦截,而不是一上来不反拦截,而是到了该拦截再去拦截,这个时候已经迟了。

    ok,说了这么多的dispatchTouchEvent(),你似乎还想说onTouchEvent()还没说,其实呢。ViewGroup中的onTouchEvent()跟view的onTouchEvent()是一样,我们放在下面来说。

    View中MotionEvent的介绍

    view的就两个方法比较重要,其实view事件传递是比较简单的。
    dispatchTouchEvent()
    onTouchEvent()
    首先看下dispatchTouchEvent()的源码,我们直接跳重点看吧

     if (onFilterTouchEventForSecurity(event)) {
                //noinspection SimplifiableIfStatement
                ListenerInfo li = mListenerInfo;
                if (li != null && li.mOnTouchListener != null
                        && (mViewFlags & ENABLED_MASK) == ENABLED
                        && li.mOnTouchListener.onTouch(this, event)) {
                    result = true;
                }
    
                if (!result && onTouchEvent(event)) {
                    result = true;
                }
            }

    dispatchTouchEvent的其他的代码,都是无关紧要的。直接看这段,可以看到先判断了是否存在了touchlistener,如果有,则执行了
    mOnTouchListener的ontouch的方法。根据返回值,决定需不要执行onTouchEvent(event),也就是说OnTouchListener的ontouch的优先级高于onTouchEvent()的方法。是不是觉得view的dispatchTouchEvent特别简单。其实这很正常,想想view是负责处理和消费事件的,所以它的dispatchTouchEvent出了分发给自己,还能怎么办?你可能会问这是不是意味着onTouchEvent()会很难,实际上它的onTouchEvent()还真不难.不信接着看下来
    onTouchEvent()核心代码

     if (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                    (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
                switch (action) {
                    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, x, y);
                           }
    
                            if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                                // 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();
                        }
                        mIgnoreNextUpEvent = false;
                        break;
    
                    case MotionEvent.ACTION_DOWN:
                        mHasPerformedLongPress = false;
    
                        if (performButtonActionOnTouchDown(event)) {
                            break;
                        }
    
                        // Walk up the hierarchy to determine if we're inside a scrolling container.
                        boolean isInScrollingContainer = isInScrollingContainer();
    
                        // For views inside a scrolling container, delay the pressed feedback for
                        // a short period in case this is a scroll.
                        if (isInScrollingContainer) {
                            mPrivateFlags |= PFLAG_PREPRESSED;
                            if (mPendingCheckForTap == null) {
                                mPendingCheckForTap = new CheckForTap();
                            }
                            mPendingCheckForTap.x = event.getX();
                            mPendingCheckForTap.y = event.getY();
                            postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                        } else {
                            // Not inside a scrolling container, so show the feedback right away
                            setPressed(true, x, y);
                            checkForLongClick(0);
                        }
                        break;
    
                    case MotionEvent.ACTION_CANCEL:
                        setPressed(false);
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
    
                    case MotionEvent.ACTION_MOVE:
                        drawableHotspotChanged(x, y);
    
                        // Be lenient about moving outside of buttons
                        if (!pointInView(x, y, mTouchSlop)) {
                            // Outside button
                            removeTapCallback();
                            if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                                // Remove any future long press/tap checks
                                removeLongPressCallback();
    
                                setPressed(false);
                            }
                        }
                        break;
                }
    
                return true;
            }

    看了上面的代码是不是觉得很操蛋,但是你注意到了最后return true。这个是不是很有趣,我不管里面具体实现是什么,只要你进入了if里面就时代表你处理并消费了事件。好的,我们看下进入if的条件viewFlags & CLICKABLE) == CLICKABLE ||
    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
    (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE,我翻译直白的点,其实就是判断这个view时可点击的,可长按的,还有一个我也不知道,好的,记住前两个就行了。换句话说,只要这个view是可点击的,可长按的,这两个属性怎么设置的呢?第一种,当你给他她们设置了setOnClickListener,或者setOnLongClickListener(),这个时候已经设置可点击了或者可长按的
    view中 setOnLongClickListener的源码。

    public void setOnLongClickListener(@Nullable OnLongClickListener l) {
            if (!isLongClickable()) {
                setLongClickable(true);
            }
            getListenerInfo().mOnLongClickListener = l;
        }

    从设置监听我们就可以看到第二种方法setLongClickable()和setClickable();第三种方法,xml android:clickable="true"
    android:longClickable="true"

    默认的Clickable或者LongClickable为true还是有一些控件的,比如button,checkbox等。
    好了上面已经说明了如何进入这个if语句中,也就是如何让view去消费这个事件,关于if的里面的代码逻辑,其实就是在up的时候判断是否可以触发点击事件,在move的时候是否能触发长按事件。
    最后说一点,判断一个view能否消费事件仅仅只是判断是否可点击或者可长按的,与是不是enable没关系。

    总结

    1 viewgroup的dispatchTouchEvent()的伪代码如下

    public boolean dispatchTouchEvent(MotionEvent event){
        boolean handle = false;
        if(!onInterceptTouchEvent(event) || requestDisallowInterceptTouchEvent(event)&& event.getActionCode != MotionEvent.ACTION_DOWN){
        handle = child.dispatchEvent();
        }
        if(!handle){
            handle = onTouchEvent();
        }
        return handle;
    }

    2 viewgroup一开始就拦截向child传递的motionEvent,如果他onTouchEvent()没有消费掉事件,则这个系列以后的事件都没办法传递到它这儿
    3 viewgroup一开始不拦截向child传递的motionEvent,并且child成功的消费了motionEvent,则当拦截以后,这个系列的事件以后的事件都能能传到viewgroup这个,尽管它并没有消费掉这个事件
    4 如果child想反拦截,那么首先第一必须得down事件传递下去并消费掉,使得接下来的事件能够传递下去。
    5 反拦截的顺序一定要注意先后,详情请好好看看viewgroup的例子6,
    6 view中onTouchListener的执行顺行要先于onClickListener和onLongChilckListener,并且onTouchListener中onTouch()返回值决定需不需要执行onClickListener和onLongChilckListener。
    7 view是否消费事件,仅仅由自己的clickable属性和LongClickable的属性有关。

    展开全文
  • 在使用VC开发软件的过程中,正当要享受那种兴奋的时候突然发现:release与debug运行结果不一致甚至出错,而release又不方便调试,真的是当头一棒啊,可是疼归疼,问题总要解决,下面将讲述一下我的几点经验,看看...

    在使用VC开发软件的过程中,正当要享受那种兴奋的时候突然发现:release与debug运行结果不一致

    甚至出错,而release又不方便调试,真的是当头一棒啊,可是疼归疼,问题总要解决,下面将讲述一下我的几点经验,看看是不是其中之一:


    1. 变量。
     大家都知道,debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc(注1),而release的赋值近似于随机(我想是直接从内存中分配的,没有初始化过)。这样就明确了,如果你的程序中的某个变量没被初始化就被引用,就很有可能出现异常:用作控制变量将导致流程导向不一致;用作数组下标将会使程序崩溃;更加可能是造成其他变量的不准确而引起其他的错误。所以在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。代码存在错误在debug方式下可能会忽略而不被察觉到,如debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了:( 还是自己多加注意吧

    2. 自定义消息的消息参数。
     MFC为我们提供了很好的消息机制,更增加了自定义消息,好处我就不用多说了。这也存在debug跟release的问题吗?答案是肯定的。在自定义消息的函数体声明时,时常会看到这样的写法:afx_msg LRESULT OnMessageOwn(); Debug情况下一般不会有任何问题,而当你在Release下且多线程或进程间使用了消息传递时就会导致无效句柄之类的错误。导致这个错误直接原因是消息体的参数没有添加,即应该写成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); (注2)

    3. release模式下不出错,但debug模式下报错。
     这种情况下大多也是因为代码书写不正确引起的,查看MFC的源码,可以发现好多ASSERT的语句(断言),这个宏只是在debug模式下才有效,那么就清楚了,release版不报错是忽略了错误而不是没有错误,这可能存在很大的隐患,因为是Debug模式下,比较方便调试,好好的检查自己的代码,再此就不多说了。

    4. ASSERT, VERIFY, TRACE..........调试宏
    这种情况很容易解释。举个例子:请在VC下输入ASSERT然后选中按F12跳到宏定义的地方,这里你就能够发现Debug中ASSERT要执行AfxAssertFailedLine,而Release下的宏定义却为"#define ASSERT(f) ((void)0)"。所以注意在这些调试宏的语句不要用程序相关变量如i++写操作的语句。VERIFY是个例外,"#define VERIFY(f) ((void)(f))",即执行,这里的作用就不多追究了,有兴趣可自己研究:)。


    总结:
     Debug与Release不同的问题在刚开始编写代码时会经常发生,99%是因为你的代码书写错误而导致的,所以不要动不动就说系统问题或编译器问题,努力找找自己的原因才是根本。我从前就常常遇到这情况,经历过一次次的教训后我就开始注意了,现在我所写过的代码我已经好久没遇到这种问题了。下面是几个避免的方面,即使没有这种问题也应注意一下:

    1. 注意变量的初始化,尤其是指针变量,数组变量的初始化(很大的情况下另作考虑了)。
    2. 自定义消息及其他声明的标准写法
    3. 使用调试宏时使用后最好注释掉
    4. 尽量使用try - catch(...)
    5. 尽量使用模块,不但表达清楚而且方便调试。

    注1:
     debug版初始化成0xcc是因为0xcc在x86下是一条int 3单步中断指令,这样程序如果跑飞了遇到0xcc就会停下来,这和单片机编程时一般将没用的代码空间填入jmp 0000语句是一样地

    注2:
     不知大家有没有遇到过这种情况,具体原因我也不太清楚,是不是调用时按着默认的参数多分配了WPARAM+LPARAM的空间而破坏了应用程序的内存空间?还请高手来补充。

    NightWolf 网友提供:我遇见过,好像是在函数调用的时候参数入栈的问题。因为MFC的消息使用宏写的,所以如果定义了OnMessage()的函数,编译能够通过,但是调用一次后,堆栈指针发生了偏移。 

    展开全文
  • Android面试题-兴奋了有木有

    千次阅读 2012-08-14 17:37:46
    Android面试题-兴奋了有木有 什么是Activity? 请描述一下Activity生命周期。 答:创建 onCreate - 启动onStart – 开始 onResume – 暂停 onPause – 结束 onStop –销毁onDestroy 两个Activity之间跳转时必然...
  • 在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,本文主要讲述Java参数传递机制分析:值与引用,更多Java专业知识,广州疯狂java培训为你...
  • 能解决这个问题真的太让人兴奋,这里要普及一个知识点,那就是所谓的序列化。 序列化:将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地...
  • 在一开始开发过程中,并没有遇到什么问题。 随着开发进程的深入,到了支付这步时,客户需要接入微信支付。 以下是我遇到过的问题以及我的解决方法。 一、使用官方的Demo以及出现的错误(不想改的话可以下载我提供...
  • 神经元-突触传递

    2012-07-02 11:02:15
    如果这种作用足够大时,即可引起突触后神经元发生兴奋或抑制反应。  单胺类递质的神经元的突触传递另有一种方式。这类神经元的轴突末梢有许多分支,在分支上有大量的结节状曲张体。曲张体内含有大量的小泡,是递...
  • 6月4号,对长沙人来说是很兴奋的一天,因为奥运火炬今天会在长沙传递。我们学校5点半就准时从学校的北门出发,走十五里到咸嘉新村去,那里是我们今天的目的地。长长的队伍出发了,从头看不到尾,每个人都穿着白色的T...
  • Nutch数据在各模块间的传递

    千次阅读 2012-06-10 16:15:12
    传递给JobConf就Ok了,由此可以看出Reduce过程对于同一个Key只被调用一次,那个values的Iterator包含这个Key所对应的全部记录,你可以简单的只取第一条数据,对这些记录进行比较,得到你认为最有效的一条记录,...
  • (4)池化(Pooling) 池化的目的是提取特征,减少向下一个阶段传递的数据量。池化操作相对于对每个深度切片是独立,池化规模一般为像素的 2*2,与卷积运算相比,池化层运算一般有以下几种: 最大池化(Max ...
  • 为什么Java小伙对Node.js和JavaScript 如此兴奋? – David Herron , 软件工程师和作家,热衷于Node.js 以及清洁能源技术。Node.js WEB 开发网站的作者。https://sourcerer.io/robogeek 在Sun Microsystems 公司...
  • 至顶网CIO与应用频道 01月22日 编译:随着人们对大数据的预测和预期不断加速,企业数据团队发现自己正处于...对于刚加入大数据热潮和已经充分涉足的企业,我们总结了:四个值得兴奋的理由:- 机器学习方法变得更容易获
  • Delphi7下用dbExpress调用Oracle存储过程(返回数据集)的一个简单示例和调试过程关键字:Delphi7 dbExpress Oracle 存储过程 游标一、问题缘起: 昨天晚上在网络上偶遇jcc老兄,谈起一个月前的一个帖子。...
  • 人工智能思维的过程是什么?

    千次阅读 2019-01-28 13:18:53
    就目前而言,人工智能是一个十分火热的事物,给我们的生活中很多东西赋予了智能化,极大地方便了我们的生活,人工智能机就是让物体像人的大脑一样去思考去解决一些问题,但是大家是否知道人工智能思维的过程是什么呢...
  • 通过该文章,你可以学习到如何配置kernel,如何交叉编译kernel,如何配置busybox并编译,如何制作initramfs,如何制作根文件系统,如何定制自己的uboot,如何通过uboot向kernel传递参数等。开始干活!1 环境搭建在...
  • 利用qemu模拟嵌入式系统制作全过程

    千次阅读 2014-11-12 12:24:50
    利用qemu模拟嵌入式系统制作全过程 tinylab.org/using-qemu-simulation-inserts-the-type-system-to-produce-the-whole-process/ Wen Pingbo 目录 1 利用qemu模拟嵌入式系统制作全过程 1.1 ...
  • 这篇文章主要介绍如何用 ...通过该文可以学习到如何配置和交叉编译 Linux Kernel,如何配置 Busybox 并编译,如何制作 Initramfs,如何制作根文件系统,如何定制自己的 Uboot,如何通过 Uboot 向 Kernel 传递参数等。
  • 1.图像的输入 一张照片是如何输入到神经网络中的。众所周知,计算机适合处理的是矩阵运算,所以...与其他神经网络相同,CNN网络同样也包含输入层、隐藏层、输出层几大部分,卷积神经网络的主要运算过程如图 卷积.
  • Linux启动过程分析简略分析, start_kernel部分代码阅读
  • CANoe仿真CAN总线过程一些小理解

    千次阅读 2019-03-12 09:02:46
    我没有惊讶,反倒是觉得有些兴奋:兴许我遇上了个好老师。虽然后面我发现,除了三毛的书之外,其他的文学作品,我几乎是看不下去的。 关于CANoe这个软件简介:  CANoe是Vector公司开发的一款can总线开发、...
  • 品牌LOGO设计的过程、要求及方法 绝妙的logo设计与品牌设计并非出自偶然,它们出自精心审慎的设计、创意,来源于品牌的价值、梦想、承诺及营销目标的深思熟虑而碰撞出的火花。设计logo包含一定的程序,熟练掌握这一...
  • 与动作电位不同,不是可传递神经信息的表达形式,只能引起感受器或突触后膜的局部兴奋性变化。这种变化可以发生时间和空间总和,最终导致突触后神经元的发放或与此相联系的感觉神经元的兴奋。 Neuron action ...
  • 上传图片之全过程

    千次阅读 2009-06-17 20:25:00
    但是当开始真正做项目时才意识到了这真是一个难题, 到现在我还没有试验成功, 将整个过程记录一下:   1 、 首先考虑直接往数据库中插入图片,参考了很多资料前后忙了将近两个小时 后来放弃了, 一则是这样子...
  • //数据库中实际存在这个测试存储过程,select * from atable,不传递参数 Cmd->CommandType = adCmdStoredProc; Cmd->Parameters->Refresh(); Rs = Cmd->Execute( NULL,NULL, adCmdUnknown ); //COM出错。 //...
  • 基于IE内核的多标签浏览器开发过程

    千次阅读 热门讨论 2014-03-23 00:16:40
    等按照上边的想法开发完成,正在兴奋的时候。 不幸的事发生了,Web页面开多了之后,速度很慢,而且很不稳定。 在WIN7下,当Web页面达到10多个之后,程序崩溃,报告说什么资源不足。 在WINXP的情况更加糟糕,开...
  • 过程 1】原则 原则对于任何一项技术实现来说都是至关重要的,在设计某一个系统功能的时候我们讲究的是设计原则: 【单一职责原则Single Responsibility Principle、里氏替换原则Liskov Substitution ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,727
精华内容 5,090
关键字:

兴奋传递过程