精华内容
下载资源
问答
  • 下面小编就为大家带来一篇Android onTouchEvent事件中onTouch方法返回值(介绍)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • onTach介绍 ontach是Android系统中整个事件机制的基础。Android中的其他事件,如onClick、onLongClick等都是以onTach为基础的。 onTach包括从手指按下到离开手机屏幕的整个过程,在微观形式上,具体表现为action_...
  • 1.onTouch和onTouchEvent,都是在dispatchTouchEvent()中调用,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。另外需要注意的是,onTouch能够得到执行...
  • onTouch先执行,还是onClick执行?下面小编就为大家带来一篇浅谈onTouch先执行,还是onClick执行(详解)。希望对大家有所帮助。一起跟随小编过来看看吧
  • onTouch

    2017-04-07 21:20:00
    OnTouch OmOnTouchListener OnTouchEvent View的事件分发 :  对于事件分发机制,举个简单的例子,在一个Activity中只有一个按钮,如果我们想给这个按钮注册一个点击事件,只需要调用setOnClickListener方法,这样在...

    OnTouch
    OmOnTouchListener
    OnTouchEvent

    View的事件分发 :
        对于事件分发机制,举个简单的例子,在一个Activity中只有一个按钮,如果我们想给这个按钮注册一个点击事件,只需要调用setOnClickListener方法,这样在onClick方法里
        面写实现的代码,就可以在按钮被点击的时候执行.我们再给这个按钮添加一个touch事件,只需要调用setOnTouchListener方法,onTouch方法里能做的事情比onClick要多一
        些,比如判断手指按下、抬起、移动等事件。如果我两个事件都注册了,我之前做过一个实验,运行程序点击按钮的结果是onTouch是优先于onClick执行的,并且onTouch执行
        了两次,一次是ACTION_DOWN,一次是ACTION_UP(你还可能会有多次ACTION_MOVE的执行,如果你手抖了一下)。因此事件传递的顺序是先经过onTouch,再传递到onClick。
        onTouch方法是有返回值的,如果我们尝试把onTouch方法里的返回值改成true,再运行一次就会发现onClick方法不再执行了,这是因为onTouch方法返回true就认为这个事件
        被onTouch消费掉了,因而不会再继续向下传递。


    1、为什么要有事件分发机制
        如果ViewGroupA里面包含一个ViewGroupB,ViewGroupB里面再包含一个view,
        当点击view的时候,为什么只有view相应事件,点击的也是A的区域。
        这就引发了事件分发机制

    2、MotionEvent主要有几个事件类型、
        down
        move
        up

    3、事件分发的主要方法
        分发事件:
            dispatchTouchEvent(View,ViewGroup:view 之间touch事件传递的入口函数 onInterceptTouchEvent:判断 touch 事件是否拦截 onTouchEvent:处理 touch 事件)
            如果返回true/false,则表示事件传递到这儿就终止了,不会继续分发
            也不会传给父级

        拦截事件:onInterceptTouchEvent
            只有ViewGroup有这个方法,只有子类才需要拦截。返回true,表示拦截该事件
            就不会分发给子类。

        消费事件:onTouchEvent
            Touch 事件 ACTIONDOWN, ACTIONUP ,ACTIONMOVE, ACTIONCANCLE
            返回true,这个事件被消费掉了,就不会再传给出去了。如果没有人消费掉,则最终
            会被当前的activity消耗掉,下次的move和up事件就不会在传下去了。

        注意:
            一个down事件分发之后,还有回传的过程。因为一个事件分发包括down、move、
            up这个几个动作,当手指触碰到屏幕那一刻,首先分发down事件,事件分发完之
            后还要回传回去,然后继续从头开始分发,执行下一个move操作,知道执行完up
            事件,整个分发过程到此结束。

            当自定义viewgroup时不会拦截down事件,一旦拦截,后边的move和up就不会再
            传递下去了。事件以后只会传给viewgroup这里。

        事件一旦分发到view则默认一定会执行他的onTouchEvent分发
            父view把touch 事件传递给子view 处理,是调用了子view 的 dispatchTouchEvent 方法
            子view的 dispatchTouchEvent 方法把 touch 事件交给两个兄弟负责:
            mOnTouchListener.onTouch 方法
            onTouchEvent 方法
            先是让 onTouch 方法处理 touch 事件,如果 onTouch 方法消耗掉 touch 事件,那么 dispatchTouchEvent 方法直接返回 true;如果 onTouch 方法没有消耗掉
            touch 事件,那么调用 onTouchEvent 方法来处理 touch 事件,此时 dispachTouchEvent 方法的返回值,就靠 onTouchEvent 方法来决定。

    列举:
        A包括B,B包括C(view)详解:
        1、点击c,c不消费事件
        当一个事件产生后,传递流程是  activity --> phoneWindow --> view
        当点击c的时候,事件从mainActivity的事件分发开始,然后去问b是否消费事件,然后将事件
        分发给B,事件到了B,他不拦截事件,先去问自己的孩子要不要消费事件,然后发给c,事件
        到了view,开始执行dispatchevent方法来决定是否消费事件。
        事件回传, c不消费事件,所以它开始回传,告诉B我不消费事件(view的onTouchEvent返回false),然
        后B才有权利决定是否消费该事件,这时开始执行onTouchEvent事件,B也不消费事件,所以
        onTouchEvent也返回false,事件继续传给A的onTouchEvent方法,由于A也不消费,最终扔给
        了mainActivity去消费。

        2、点击C,view消费掉事件
        mainActivity一层一层往下传,到了view便开始执行onTouchEvent方法来决定是否消费该事件,
        由于view消费掉了这个事件,view的onTouchEvent便return false,B收到通知view消费了事
        件,所以就不会执行B的onTouchEvent事件,只能回传告诉A,view消费了事件,你没机会了。
        A收到return true后,就知道事件被消费了,所以他也return true,最终事件传给
        了mainActivity,由于事件被消费了,所以不会执行mainActivity的onTouchEvent方法,
        接下来执行Move事件,流程和之前的一样,重新开始执行。
        结论:onTouchEvent被view消费掉,A、B的onTouchEvent都不会执行。

        3、点击B,但是B不消费事件。
        B和A都不消费事件,最终只能交给mainActivity了去消费了。
        view的mOnTouchListener.onTouch方法优先于view的onTouchEvent方法。
             onTouchEvent 方法主要实现了 3 个功能:
             1. 根据 touch 事件产生 click 事件,调用 onClickListener.onclick 方法
             2. 根据 touch 事件产生 press 状态的变化
             3. 根据 touch 事件产生 焦点分发
        
    view和viewgroup对onTouchEvent事件的处理什么不同和联系?



    代码说明:

    请父级View不拦截touch事件:

        1.解决SwipeLayout和ListView的触摸事件冲突问题
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        mDownX = event.getRawX();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        float deltaX = event.getRawX() - mDownX;
                        if(Math.abs(deltaX) > mHelper.getTouchSlop()){
                            getParent().requestDisallowInterceptTouchEvent(true);//请父级View不拦截touch事件
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        mDownX = 0;
                        break;
                }
                try {
                    mHelper.processTouchEvent(event);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }

    touch事件分发的三个问题:

        1. onTouch和onTouchEvent有什么区别,又该如何使用?
            从源码中可以看出,这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,
            onTouchEvent将不会再执行。
            另外需要注意的是,onTouch能够得到执行需要两个前提条件,第一mOnTouchListener的值不能为空,第二当前点击的控件必须是enable的。因此如果我们有一个控件
            是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实
            现。

        2. 为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?
            如果你阅读了http://blog.csdn.net/guolin_blog/article/details/8744400 滑动菜单的功能是通过给ListView注册了一个touch事件来实现的。如果我们在onTouch方
            法里处理完了滑动逻辑后返回true,那么ListView本身的滚动事件就被屏蔽了,自然也就无法滑动(原理同前面例子中按钮不能点击),因此解决办法就是在onTouch方法
            里返回false。

        3. 为什么图片轮播器里的图片使用Button而不用ImageView?
            提这个问题的朋友可看看http://blog.csdn.net/guolin_blog/article/details/8769904 这篇文章。这篇文章里,在图片轮播器里使用Button,主要就是因为Button是  
            可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其
            它action都能得到执行,才能实现图片滚动的效果。第二,在布局文件里面给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后
             ,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。


    ZHBJ中...
    触摸事件的处理:
        我认为触摸事件的处理实质上就是重写控件的  
        dispatchTouchEvent(),
        onInterceptTouchEvent(),
        onTouchEvent()方法
        处理控件的分发,拦截,触摸的逻辑,用以达到我们想要的目的.以我之前做过的项目为例,
        其中比较难的有两个,一个是三次ViewPager嵌套,一个是ScrollView嵌套ViewPager再嵌套Viewpager和ListView

        >>>>>>>
        你看这个项目,它就是有三次ViewPager嵌套    
        他的最外层界面其实是一个VIewPager(其实它也可以用Fragment来做,不过我考虑到他是要不断切换界面这点和ViewPager的特点很像于是就用VIewPager来做了),我重写了它
        的onTouchEvent()方法,只返回true,屏蔽了它的滑动事件,并且设置了切换界面不滑动的属性,
        这点比较有意思,同样实现相同的功能,

        用ViewPager做我们就想屏蔽它的界面切换效果,用fragment来做又想自己给它添加一个界面切换的动画).然后把界面的切换交给了界面下方的四个单选按钮实现(这个功能的
        实现方法有很多,可以写四个TextView,然后drawableTop画上图片或者用线布局上面放ImageView下面放TextView实现都可以,只是考虑到功能需求是只能单选,和单选按钮的
        功能最为相似,用它做最简单,

        所有我使用一个RadioGroup嵌套四个RadioButton并且设置Radiobutton的button属性为null取消单选按钮的圆圈,drawableTop画上图片,给图片和字体颜色设置选择器实现的)
        ,并且把它的拦截事件onInterceptTouchEvent()方法重写返回false,让它不拦截子View的触摸事件(这个方法在ViewGroup中实现都比较简单,都是直接返回false,不过ViewPa
         ger和ListView都重写了这个方法,做了大量的逻辑和判断).

        其次,社区界面的发现和关注也是用ViewPager实现的,由于它的父控件的onInterceptTouchEvent()拦截方法已经被我重写并且返回false,所以它可接收到父控件传递过来的
        触摸事件并对其处理,所以我并没有对这层ViewPager做什么特殊的处理,直接使用了系统V4包里原生的ViewPager

        然后这个ViewPager里面的布局实际是一个Listview,前面几个比较特殊的界面是作为头布局添加进去的,这个头布局以及listview的条目中都有VIewPager的存在,这种情况就
        比较复杂了,
        因为这种ViewPager有很多控件都和它争抢触摸事件,比如它的父控件,listview,于是我做了这样的处理,首先ViewPager肯定是上下滑动不了的,然后只要ViewPager不是最后
        一个条目,我左右滑动都可以切换当前ViewPager,如果是最后一个条目的话,我再向右滑就可以切换父控件的ViewPager.所以我就重写了它的dispatchTouchEvent方法(),(虽然
        这里只有父ViewPager两个条目,但是我还是按照有多数pager的情况下写的以防以后业务发生改变,增加了条目),如果不是上下滑动,第一个条目右滑,最后一个条目左滑,我请
        求父控件不阻拦其触摸事件,让这个ViewPager处理滑动事件否则就不让父控件不阻拦,让父控件走自己的逻辑.

        而这个项目是我协助我朋友做的,它涉及到ScollView嵌套ViewPager,它这里面还用到了很多自定义控件
     

    展开全文
  • 下面小编就为大家带来一篇详谈Android中onTouch与onClick事件的关系(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 使用OnTouch实现一个控件同时设置单击、双击、长按事件监听。
  • Android onTouch事件传递机制
  • 此Demo是我进行改进的,既可以通过OnTouch对图片进行放大缩小移动,也可以对图片进行OnClick点击,OnLongClick点击。不会出现冲突。
  • Android onTouch机制

    2016-04-05 17:11:17
    Android onTouch机制源码,重写dispatchTouchEvent和onTouchEvent事件,通过日志观察执行顺序
  • onTouch事件的传递思维导图(完整版)
  • 可直接运行的手势操作onTouch事件的demo
  • 【转】Android onTouch()和onTouchEvent()区别 1、onTouch()方法:  onTouch方式是View的OnTouchListener接口中定义的方法。  当一个View绑定了OnTouchListener后,当有Touch事件触发时,就会调用onTouch方法。...

    【转】Android onTouch()和onTouchEvent()区别

    1、onTouch()方法:

      onTouch方式是View的OnTouchListener接口中定义的方法。

      当一个View绑定了OnTouchListener后,当有Touch事件触发时,就会调用onTouch方法。

      (当把手放到View上后,onTouch方法被一遍一遍的调用)

     

    2、onTouchEvent()方法:

      onTouchEvent方法时重载的Activity的方法

      重写了Acitivity的onTouchEvent方法后,当屏幕有Touch事件时,此方法就会被调用。

      (当把手放到Activity上时,onTouchEvent方法会一遍一遍的被调用)

     

    3、Touch事件的传递:

      在一个Activity里面放一个TextView的实例tv,并且这个tv的属性设定为march_parent

      在这种情况下,当手放到屏幕上的时候,首先会是tv响应Touch事件,执行onTouch方法。

     

      如果onTouch返回值为true,表示这个Touch事件被onTouch方法处理完毕,不会把Touch事件再传递给Activity

      也就是说onTouchEvent方法不会被调用

      (手放到屏幕上后,onTouch方法会被一遍一遍的调用)

     

     

      如果onTouch返回值为false,表示这个Touch事件没有被tv完全处理,onTouch返回以后,Touch事件被传递给Activity,

      onTouchEvent方法调用

      (当把手放到屏幕上后,onTouch方法调用一次后,onTouchEvent方法被一遍一遍的调用)

     

    测试:

    1、MyLinearLayout继承LinearLayout,并重写onTouchEvent方法

     

    复制代码

     1 import android.content.Context;
     2 import android.util.AttributeSet;
     3 import android.util.Log;
     4 import android.view.MotionEvent;
     5 import android.widget.LinearLayout;
     6 
     7 public class MyLinearLayout extends LinearLayout{
     8 
     9     public MyLinearLayout(Context context, AttributeSet attrs) {
    10         super(context, attrs);
    11     }
    12 
    13     @Override
    14     public boolean onTouchEvent(MotionEvent event) {
    15         switch (event.getAction()) {
    16         case MotionEvent.ACTION_DOWN://0
    17             Log.e("TAG", "LinearLayout onTouchEvent 按住");
    18             break;
    19         case MotionEvent.ACTION_UP://1
    20             Log.e("TAG", "LinearLayout onTouchEvent onTouch抬起");
    21             break;
    22         case MotionEvent.ACTION_MOVE://2
    23             Log.e("TAG", "LinearLayout onTouchEvent 移动");
    24             break;    
    25         }
    26         return super.onTouchEvent(event);
    27     }
    28     
    29     @Override
    30     public boolean dispatchTouchEvent(MotionEvent ev) {
    31         return super.dispatchTouchEvent(ev);
    32     }
    33     
    34     @Override
    35     public void setOnTouchListener(OnTouchListener l) {
    36         super.setOnTouchListener(l);
    37     }
    38     
    39 }

    复制代码

     

     

    2、在MainActivity中声明MyLinearLayout,并设置触摸监听和点击监听,同时MainActivity自己也有onTouchEvent方法,重写看结果

    复制代码

     1 import android.app.Activity;
     2 import android.os.Bundle;
     3 import android.util.Log;
     4 import android.view.MotionEvent;
     5 import android.view.View;
     6 import android.view.View.OnClickListener;
     7 import android.view.View.OnTouchListener;
     8 
     9 public class MainActivity extends Activity {
    10 
    11     MyLinearLayout ll;
    12     @Override
    13     protected void onCreate(Bundle savedInstanceState) {
    14         super.onCreate(savedInstanceState);
    15         setContentView(R.layout.activity_main);
    16         
    17         ll = (MyLinearLayout) findViewById(R.id.ll);
    18         //触摸监听
    19         ll.setOnTouchListener(new OnTouchListener() {
    20             
    21             @Override
    22             public boolean onTouch(View v, MotionEvent event) {
    23                 //Log.e("TAG", event.getX()+" "+event.getY());
    24                 
    25                 switch (event.getAction()) {
    26                 case MotionEvent.ACTION_DOWN://0
    27                     Log.e("TAG", "LinearLayout onTouch按住");
    28                     break;
    29                 case MotionEvent.ACTION_UP://1
    30                     Log.e("TAG", "LinearLayout onTouch抬起");
    31                     break;
    32                 case MotionEvent.ACTION_MOVE://2
    33                     Log.e("TAG", "LinearLayout onTouch移动");
    34                     break;    
    35                 }
    36                 //事件分发
    37                 //1、setOnTouchListener单独使用的时候返回值需要true,这样才能保证移动的时候获取相应的监听,而非一次监听(即只有按下事件)
    38                     //返回false,表示没有被处理,将向父View传递。只能监听到view的"按下"事件,"移动"和"抬起"都不能监听到。因为down事件未结束
    39                     //返回true,消耗此事件,表示正确接收并处理,不在分发。"按下""抬起""移动"都能监听到了
    40                 
    41                 //2、setOnTouchListener和setOnClickListener同时使用时,
    42                     //返回true,事件被onTouch消耗掉了,因而不会在继续向下传递。只能监听"按下""抬起""移动",不能监听到"点击";
    43                     //返回false,"按下""抬起""移动""点击"都能监听
    44                  
    45                 return false;
    46                 /**
    47                  * onTouch是优先于onClick的,并且执行了两次,一次是ACTION_DOWN,一次是ACTION_UP(可能还会有多次ACTION_MOVE),
    48                  * 因此事件传递的顺序是先经过OnTouch,再传递给onClick
    49                  *
    50                  */
    51             }
    52         });
    53         
    54         ll.setOnClickListener(new OnClickListener() {
    55             @Override
    56             public void onClick(View v) {
    57                 Log.e("TAG", "onClick点击事件");
    58             }
    59         });
    60     }
    61     
    62     //事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
    63     //当监听到事件时,首先由Activity的捕获到,进入事件分发处理流程,无论是Activity还是View,事件分发自身也具有消费能力
    64     //如果事件分发返回true,表示该事件在本层不再进行分发且已经已经在事件分发自身中被消费了。
    65     //如果你不想Activity中的任何控件具有任何的事件消费能力,可以直接重写Activity的dispatchTouchEvent方法,返回true就可以了
    66     @Override
    67     public boolean dispatchTouchEvent(MotionEvent ev) {
    68         // TODO Auto-generated method stub
    69         return super.dispatchTouchEvent(ev);
    70     }
    71     
    72     
    73     @Override
    74     public boolean onTouchEvent(MotionEvent event) {
    75         switch (event.getAction()) {
    76         case MotionEvent.ACTION_DOWN://0
    77             Log.e("TAG", "Activity onTouchEvent按住");
    78             break;
    79         case MotionEvent.ACTION_UP://1
    80             Log.e("TAG", "Activity onTouchEvent抬起");
    81             break;
    82         case MotionEvent.ACTION_MOVE://2
    83             Log.e("TAG", "Activity onTouchEvent移动");
    84             break;    
    85         }
    86         return super.onTouchEvent(event);
    87     }
    88 
    89     
    90 }

    复制代码

     

    3、由于MyLinearLayout继承了LinearLayou,但是XML并不认识他,所以在xml中搭建布局的时候,一定要包名带类名

    复制代码

     1 <com.example.lesson6_ontouch.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2        android:id="@+id/ll"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent">
     5 
     6        <TextView
     7            android:id="@+id/tv"
     8            android:layout_width="match_parent"
     9            android:layout_height="match_parent"
    10            android:text="hsss"/>
    11 
    12 </com.example.lesson6_ontouch.MyLinearLayout>

    复制代码

     

    测试结果

    4、下面贴一位博主的分析 Android事件分发机制完全解析,带你从源码的角度彻底理解(上) 

    首先你需要知道一点,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类里的dispatchTouchEvent方法,可是你会发现Button类里并没有这个方法,那么就到它的父类TextView里去找一找,你会发现TextView里也没有这个方法,那没办法了,只好继续在TextView的父类View里找一找,这个时候你终于在View里找到了这个方法,示意图如下:

                

    然后我们来看一下View中dispatchTouchEvent方法的源码:

    复制代码

    1 public boolean dispatchTouchEvent(MotionEvent event) {  
    2     if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
    3             mOnTouchListener.onTouch(this, event)) {  
    4         return true;  
    5     }  
    6     return onTouchEvent(event);  
    7 }  

    复制代码

    我们可以看到,在这个方法内,首先是进行了一个判断,如果mOnTouchListener != null,(mViewFlags & ENABLED_MASK) == ENABLED和mOnTouchListener.onTouch(this, event)这三个条件都为真,就返回true,否则就去执行onTouchEvent(event)方法并返回。

    先看一下第一个条件,mOnTouchListener这个变量是在哪里赋值的呢?我们寻找之后在View里发现了如下方法:

     

    ?

    1

    2

    3

    public void setOnTouchListener(OnTouchListener l) { 

        mOnTouchListener = l; 

    mOnTouchListener正是在setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。

     

    第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。

    第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。

     

    现在我们可以结合前面的例子来分析一下了,首先在dispatchTouchEvent中最先执行的就是onTouch方法,因此onTouch肯定是要优先于onClick执行的,也是印证了刚刚的打印结果。而如果在onTouch方法里返回了true,就会让dispatchTouchEvent方法直接返回true,不会再继续往下执行。而打印结果也证实了如果onTouch返回true,onClick就不会再执行了。

     

     根据以上源码的分析,从原理上解释了我们前面例子的运行结果。而上面的分析还透漏出了一个重要的信息,那就是onClick的调用肯定是在onTouchEvent(event)方法中的!那我们马上来看下onTouchEvent的源码,如下所示:

     

    复制代码

      1 public boolean onTouchEvent(MotionEvent event) {
      2         final float x = event.getX();
      3         final float y = event.getY();
      4         final int viewFlags = mViewFlags;
      5         final int action = event.getAction();
      6 
      7         if ((viewFlags & ENABLED_MASK) == DISABLED) {
      8             if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
      9                 setPressed(false);
     10             }
     11             // A disabled view that is clickable still consumes the touch
     12             // events, it just doesn't respond to them.
     13             return (((viewFlags & CLICKABLE) == CLICKABLE
     14                     || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
     15                     || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
     16         }
     17         if (mTouchDelegate != null) {
     18             if (mTouchDelegate.onTouchEvent(event)) {
     19                 return true;
     20             }
     21         }
     22 
     23         if (((viewFlags & CLICKABLE) == CLICKABLE ||
     24                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
     25                 (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
     26             switch (action) {
     27                 case MotionEvent.ACTION_UP:
     28                     boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
     29                     if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
     30                         // take focus if we don't have it already and we should in
     31                         // touch mode.
     32                         boolean focusTaken = false;
     33                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
     34                             focusTaken = requestFocus();
     35                         }
     36 
     37                         if (prepressed) {
     38                             // The button is being released before we actually
     39                             // showed it as pressed.  Make it show the pressed
     40                             // state now (before scheduling the click) to ensure
     41                             // the user sees it.
     42                             setPressed(true, x, y);
     43                        }
     44 
     45                         if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
     46                             // This is a tap, so remove the longpress check
     47                             removeLongPressCallback();
     48 
     49                             // Only perform take click actions if we were in the pressed state
     50                             if (!focusTaken) {
     51                                 // Use a Runnable and post this rather than calling
     52                                 // performClick directly. This lets other visual state
     53                                 // of the view update before click actions start.
     54                                 if (mPerformClick == null) {
     55                                     mPerformClick = new PerformClick();
     56                                 }
     57                                 if (!post(mPerformClick)) {
     58                                     performClick();
     59                                 }
     60                             }
     61                         }
     62 
     63                         if (mUnsetPressedState == null) {
     64                             mUnsetPressedState = new UnsetPressedState();
     65                         }
     66 
     67                         if (prepressed) {
     68                             postDelayed(mUnsetPressedState,
     69                                     ViewConfiguration.getPressedStateDuration());
     70                         } else if (!post(mUnsetPressedState)) {
     71                             // If the post failed, unpress right now
     72                             mUnsetPressedState.run();
     73                         }
     74 
     75                         removeTapCallback();
     76                     }
     77                     mIgnoreNextUpEvent = false;
     78                     break;
     79 
     80                 case MotionEvent.ACTION_DOWN:
     81                     mHasPerformedLongPress = false;
     82 
     83                     if (performButtonActionOnTouchDown(event)) {
     84                         break;
     85                     }
     86 
     87                     // Walk up the hierarchy to determine if we're inside a scrolling container.
     88                     boolean isInScrollingContainer = isInScrollingContainer();
     89 
     90                     // For views inside a scrolling container, delay the pressed feedback for
     91                     // a short period in case this is a scroll.
     92                     if (isInScrollingContainer) {
     93                         mPrivateFlags |= PFLAG_PREPRESSED;
     94                         if (mPendingCheckForTap == null) {
     95                             mPendingCheckForTap = new CheckForTap();
     96                         }
     97                         mPendingCheckForTap.x = event.getX();
     98                         mPendingCheckForTap.y = event.getY();
     99                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
    100                     } else {
    101                         // Not inside a scrolling container, so show the feedback right away
    102                         setPressed(true, x, y);
    103                         checkForLongClick(0, x, y);
    104                     }
    105                     break;
    106 
    107                 case MotionEvent.ACTION_CANCEL:
    108                     setPressed(false);
    109                     removeTapCallback();
    110                     removeLongPressCallback();
    111                     mInContextButtonPress = false;
    112                     mHasPerformedLongPress = false;
    113                     mIgnoreNextUpEvent = false;
    114                     break;
    115 
    116                 case MotionEvent.ACTION_MOVE:
    117                     drawableHotspotChanged(x, y);
    118 
    119                     // Be lenient about moving outside of buttons
    120                     if (!pointInView(x, y, mTouchSlop)) {
    121                         // Outside button
    122                         removeTapCallback();
    123                         if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
    124                             // Remove any future long press/tap checks
    125                             removeLongPressCallback();
    126 
    127                             setPressed(false);
    128                         }
    129                     }
    130                     break;
    131             }
    132 
    133             return true;
    134         }
    135 
    136         return false;
    137     }

    复制代码

     

    相较于刚才的dispatchTouchEvent方法,onTouchEvent方法复杂了很多,不过没关系,我们只挑重点看就可以了。

     

    首先在第23行我们可以看出,如果该控件是可以点击的就会进入到第26行的switch判断中去,而如果当前的事件是抬起手指,则会进入到MotionEvent.ACTION_UP这个case当中。在经过种种判断之后,会执行到第58行的performClick()方法,那我们进入到这个方法里瞧一瞧:

    复制代码

    1 public boolean performClick() {  
    2     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
    3     if (mOnClickListener != null) {  
    4         playSoundEffect(SoundEffectConstants.CLICK);  
    5         mOnClickListener.onClick(this);  
    6         return true;  
    7     }  
    8     return false;  
    9 }  

    复制代码

    可以看到,只要mOnClickListener不是null,就会去调用它的onClick方法,那mOnClickListener又是在哪里赋值的呢?经过寻找后找到如下方法:

     

    复制代码

    1 public void setOnClickListener(OnClickListener l) {  
    2     if (!isClickable()) {  
    3         setClickable(true);  
    4     }  
    5     mOnClickListener = l;  
    6 } 

    复制代码

     

     一切都是那么清楚了!当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值。然后每当控件被点击时,都会在performClick()方法里回调被点击控件的onClick方法。

     

    这样View的整个事件分发的流程就让我们搞清楚了!不过别高兴的太早,现在还没结束,还有一个很重要的知识点需要说明,就是touch事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。

    说到这里,很多的朋友肯定要有巨大的疑问了。这不是在自相矛盾吗?前面的例子中,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到执行了吗?其实你只是被假象所迷惑了,让我们仔细分析一下,在前面的例子当中,我们到底返回的是什么。

    参考着我们前面分析的源码,首先在onTouch事件里返回了false,就一定会进入到onTouchEvent方法中,然后我们来看一下onTouchEvent方法的细节。由于我们点击了按钮,就会进入到第23行这个if判断的内部,然后你会发现,不管当前的action是什么,最终都一定会走到第133行,返回一个true。

    是不是有一种被欺骗的感觉?明明在onTouch事件里返回了false,系统还是在onTouchEvent方法中帮你返回了true。就因为这个原因,才使得前面的例子中ACTION_UP可以得到执行。

     

    那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。如下所示:

    复制代码

    1 imageView.setOnTouchListener(new OnTouchListener() {  
    2     @Override  
    3     public boolean onTouch(View v, MotionEvent event) {  
    4         Log.d("TAG", "onTouch execute, action " + event.getAction());  
    5         return false;  
    6     }  
    7 }); 

    复制代码

    运行一下程序,点击ImageView,你会发现结果如下:

     

     

    在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。

    好了,关于View的事件分发,我想讲的东西全都在这里了。现在我们再来回顾一下开篇时提到的那三个问题,相信每个人都会有更深一层的理解。

    1. onTouch和onTouchEvent有什么区别,又该如何使用?

    从源码中可以看出,这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。

    另外需要注意的是,onTouch能够得到执行需要两个前提条件,第一mOnTouchListener的值不能为空,第二当前点击的控件必须是enable的。因此如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现。

    2. 为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?

    如果你阅读了Android滑动框架完全解析,教你如何一分钟实现滑动菜单特效 这篇文章,你应该会知道滑动菜单的功能是通过给ListView注册了一个touch事件来实现的。如果你在onTouch方法里处理完了滑动逻辑后返回true,那么ListView本身的滚动事件就被屏蔽了,自然也就无法滑动(原理同前面例子中按钮不能点击),因此解决办法就是在onTouch方法里返回false。

    3. 为什么图片轮播器里的图片使用Button而不用ImageView?

    提这个问题的朋友是看过了Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来 这篇文章。当时我在图片轮播器里使用Button,主要就是因为Button是可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其它action都能得到执行,才能实现图片滚动的效果。第二,在布局文件里面给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。

    今天的讲解就到这里了,相信大家现在对Android事件分发机制又有了进一步的认识,在后面的文章中我会再带大家一起探究Android中ViewGroup的事件分发机制,感兴趣的朋友请继续阅读 Android事件分发机制完全解析,带你从源码的角度彻底理解(下) 。

     

    展开全文
  • OnTouch事件对应代码

    2015-02-05 22:10:31
    OnTouch事件的机制,容易出错的地方
  • 一、问题描述 当你对一个控件(例如ImageView)使用setOnTouchListener() 或者是对你的自定义控件... MyImageOnTouchListener#onTouch should call View#performClick when a click is detected less… (Ctrl+F...

    一、问题描述

    当你对一个控件(例如ImageView)使用setOnTouchListener() 或者是对你的自定义控件重写onTouchEvent方法时会出现这个警告,警告内容全文如下:

    MyImageOnTouchListener#onTouch should call View#performClick when a click is detected less… (Ctrl+F1)
    If a View that overrides onTouchEvent or uses an OnTouchListener does not also implement performClick and call it when clicks are detected, the View may not handle accessibility actions properly. Logic handling the click actions should ideally be placed in View#performClick as some accessibility services invoke performClick when a click action should occur.

    这里写图片描述

    这段英文翻译过来,大致意思如下:

    如果覆盖onTouchEvent或使用OnTouchListener的View没有实现performClick方法,并且在检测到click

    展开全文
  • 新手学习Android, ontouch事件处理的三种写法,1、接口实现,2、内部类,3、匿名内部类
  • android ontouch(记录滑动坐标)

    热门讨论 2011-12-28 17:38:19
    记录手指在屏幕上滑动的轨迹,并打印出坐标的变化过程。
  • 用于解决一个界面内同时存在多个不同类型监听冲突的方法,实现类似QQHD版窗口拖动滚动点击效果
  • 事件处理之onTouchEvent()和onTouch()方法精炼详解

    万次阅读 多人点赞 2018-05-26 12:05:21
    1.onTouchEvent()和onTouch()处理的对象是谁? 点击事件(Touch事件)。当用户触摸屏幕时(View 或 ViewGroup派生的控件),将产生点击事件(Touch事件)。常见的点击事件包括:单击、双击、触摸、滑动。 Touch...

    一、前期基础知识储备

    1.onTouchEvent()和onTouch()处理的对象是谁?

    点击事件(Touch事件)。当用户触摸屏幕时(View 或 ViewGroup派生的控件),将产生点击事件(Touch事件)。常见的点击事件包括:单击、双击、触摸、滑动。

    Touch事件的相关细节(发生触摸的位置、时间等)被封装成MotionEvent对象。

    所以传入参数之后完整的方法展示:onTouchEvent(MotionEvent event)和onTouch(View v, MotionEventevent)。参数event为手机屏幕触摸事件封装类的对象,其中封装了该事件的所有信息,例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创建。

    2. onTouchEvent()和onTouch()何时使用?

    ①onTouchEvent(MotionEvent event)是View中定义的方法,而且是Public类型,所以Activity、ViewGroup、View均可以调用这个方法,最常见的关于这块的知识点在Android事件分发中,onTouchEvent()是每个事件处理对象都有的方法,用于根据下层的onTouchEvent()返回值类型判断是否调用,最常见的用法是自定义View时写入到view中,从而让该View获取用户对手机屏幕的各种操作,并对不同类型的操作实现不同的反馈,属于一个宏观的屏幕触摸监控方法

    ②onTouch((View v, MotionEvent event)是View.OnTouchListener接口中实现的唯一方法,接收两个参数,第二个参数是之前提过的event事件对象,第一个参数是一个具体的view类型对象,这就意味着onTouch()方法必须和某个控件进行绑定,即某个控件实现了View.OnTouchListener接口,才能调用onTouch()方法。

    3. onTouchEvent()和onTouch()方法的返回值类型

    均是布尔型的返回值,所以完整的代码语句展示如下:

    ①public boolean onTouchEvent(MotionEventevent){

                                return true;

    }

    该方法的返回值是当已经完整地处理了该事件且不希望其他回调方法再次处理时返回true,否则返回false。即view如果通过onTouchEvent()方法处理了事件,不希望也不必要传到ViewGroup中时,即返回true,如果view处理不了事件,需要往上级传时,返回值为false,同理ViewGroup和Activity之间事件的控制也是如此。

    ②public boolean onTouch(View v, MotionEventevent) {

                                return true

    }

    该方法的返回值类型主要用于控制是否执行onTouchEvent()方法及onTouchEvent()方法内部内置的各种click点击事件是否执行。

    利用此特性可以单独拦截某个View的点击事件,比如

        private void initView() {
            Button btn_onTouch = findViewById(R.id.test_on_touch_btn);
            LinearLayout ll_onTouch = findViewById(R.id.test_on_touch_ll);
            // 重写父布局的onTouch()方法 
            ll_onTouch .setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    // 拦截底部布局的点击事件 返回值为true 消费事件
                    return true;
                }
            });
    
            btn_onTouch.setOnClickListener(this);
            ll_onTouch .setOnClickListener(this);
    
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.test_on_touch_btn:
                    Toast.makeText(this, "点击了按钮", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.test_on_touch_ll:
                    Toast.makeText(this, "点击了布局", Toast.LENGTH_SHORT).show();
                    break;
            }
    
        }

    效果如下:

    可以看到,点击按钮红色区域按钮时,会有Toast显示;点击蓝色布局时没有,因为onTouch()方法中已经返回true,拦截了点击事件,处于事件分发末端的onClick()方法不在触发了。

    4. onTouchEvent()和onTouch()方法优先级及控制关系

    ①如果onTouch()方法返回值是true(事件被消费)时,则onTouchEvent()方法将不会被执行;

    ②只有当onTouch()方法返回值是false(事件未被消费,向下传递)时,onTouchEvent方法才被执行。

    由此可见,给View设置监听OnTouchListener时,重写的onTouch()方法,其优先级比onTouchEvent()要高,假如onTouch方法返回false,会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。内置诸如click事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发。

    可以看出,平时我们使用的OnClickListener,其优先级最低,即处于事件传递的尾端。

    二、上代码,具体实现

    1. onTouchEvent()方法使用——随手势移动view

        @Override  
        public boolean onTouchEvent(MotionEvent event) {  
            //每次回调onTouchEvent的时候,我们获取触摸点的代码  
            int x = (int) event.getX();  
            int y = (int) event.getY();  
            switch (event.getAction()) {  
                case MotionEvent.ACTION_DOWN:  
                    // 记录触摸点坐标  
                    lastX = x;  
                    lastY = y;  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    // 计算偏移量  
                    int offsetX = x - lastX;  
                    int offsetY = y - lastY;  
                    // 在当前left、top、right、bottom的基础上加上偏移量  
                    layout(getLeft() + offsetX,  
                            getTop() + offsetY,  
                            getRight() + offsetX,  
                            getBottom() + offsetY);  
                    break;  
            }  
            return true;  
        }

    自定义一个View,放置在布局文件中,同时在自定义的View中写入onTouchEvent()方法,将每次手势移动产生的坐标设置给view,从而实现view滑动的实时更新。

    2.onTouch()方法使用——随手势移动button

    类似悬浮窗效果,该button不在xml布局中写入,而是通过Java代码动态的创建该按钮,通过LayoutParams设置其横纵坐标,实现View.OnTouchListener接口,重写onTouch()方法,在里面从触摸事件中不断拿到新坐标设置给button,实现button动态滑动的效果。

     使用WindowManger实现,记得申请权限:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
        private Button btnTest2;
        private Button btnTest1;
        private WindowManager.LayoutParams layoutParams;
        private WindowManager windowManager;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            btnTest1 = (Button) findViewById(R.id.btn_test);
            windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        }
        //布局文件中的按钮写入的点击事件onButtonClick,用于控制“滑动按钮的显示”
        public void onButtonClick(View view) {
            if (view == btnTest1) {
                btnTest2 = new Button(this);
                btnTest2.setText("滑动按钮");
                layoutParams = new WindowManager.LayoutParams(
                        WindowManager.LayoutParams.WRAP_CONTENT,
                        WindowManager.LayoutParams.WRAP_CONTENT,
                        0, 0,//0,0 分别是type和flags参数,在后面分别配置了
                        PixelFormat.TRANSPARENT);
                layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
                //申明以下type时 需要申请权限android.permission.SYSTEM_ALERT_WINDOW
                layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
                layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
                layoutParams.x = 100;
                layoutParams.y = 300;
                windowManager.addView(btnTest2, layoutParams);
                btnTest2.setOnTouchListener(this);
            }
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int rawX = (int) event.getRawX();
            int rawY = (int) event.getRawY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    break;
                }
                /*依旧是是从event事件中拿到坐标 为了实现动态移动的按钮 必须动态的创建按钮 指定按钮的坐标*/
                case MotionEvent.ACTION_MOVE:
                    layoutParams.x = rawX;
                    layoutParams.y = rawY;
                    windowManager.updateViewLayout(btnTest2, layoutParams);
                    break;
                case MotionEvent.ACTION_UP: {
                    break;
                }
            }
            return false;
        }
        @Override
        protected void onDestroy() {
            try {
                windowManager.removeView(btnTest2);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            super.onDestroy();
        }
    }

     

    总结:Android开发中会遇到很多事件监听事件,例如我们常见的普通控件的OnClickListener监听事件、高级控件(ListView、RecyclerView)的OnItemClickListener监听事件、CheckBox的OnCheckedChangeListener、还有SeekBar的OnSeekBarChangeListener等等。

     

    除此之外我们常常看到一些触摸事件,例如侧滑菜单或者ViewPager的实现,那么这些监听是如何实现的呢?

    实际上以上所说的触摸事件就是我们常常听到的手势监听,代码中实现手势监听分三种,

    一种是重写Activity内部或者自定义view 内部重写onTouchEvent方法来实现全局的手势监听;

    另一种是通过某个系统控件实现OnTouchListener接口重写onTouch方法实现某一控件的手势监听;这样做的两个好处在于,不用单纯为了Touch事件去自定义控件,并且在Activity内部控件采用此处理方式,非常适合进行数据的传递

    最后一种是通过Activity全局实现OnTouchListener接口重写onTouch方法,然后在该方法内部用GestureDetector拦截掉事件:

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return mIndicatorGestureDetector.onTouchEvent(event);
        }

     

    展开全文
  • 解决了android悬浮框的onTouch和onClick事件可以同存的问题。
  • Android中onTouch方法的执行过程以及和onClick执行发生冲突的解决办法
  • 1、onTouch()方法: onTouch方式是View的OnTouchListener接口中定义的方法。 当一个View绑定了OnTouchListener后,当有Touch事件触发时,就会调用onTouch方法。 (当把手放到View上后,onTouch方法被一遍一遍的...
  • android onTouch事件处理机制

    千次阅读 2017-10-26 15:25:55
    可以看到,第一个if语句,先判断执行onTouchListener的onTouch方法,如果都返回true,代码就不再执行下一个if 语句。那onClick在在哪呢?在第二个if语句里的onTouchEvent里。 代码比较长,只要关注performClick...
  • Android开发详解之onTouch和onClick详解
  • 我们会经常在代码中看到触屏事件的处理,有时候使用onTouch,有时候使用了onTouchEvent,还有的时候使用了onClick,那么我们有没有对这三个方法进行过思考: 三者有什么区别呢? 要是三者都在代码中出现,应该是怎样的调用...

空空如也

空空如也

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

ontouch