android事件处理机制-自定义view

ViewPager,ScrollView 嵌套ViewPager滑动冲突解决

android事件处理机制-自定义view

 

这个图基本上表示出了事件处理机制,activity一般不去做处理(此图没有表示出来的就是可能出现多个ViewGroup层)。

1,主要在ViewGroup的onInterceptTouchEvent方法的拦击做处理,是否拦击自己消费true去onTouchEvent处理,

2,在View和ViewGroup2中一般就是用dispatchTouchEvent的方法分派中使用getParent().requestDisallowInterceptTouchEvent(true);来让上层ViewGroup的拦截是否消费,true是上层消费,false是上层不消费

3,在touchEvent中

  • event.getRowX:触摸点相对于屏幕的坐标
  • event.getX: 触摸点相对于按钮的坐标
  • view.getTop: 按钮左上角相对于父view(LinerLayout)的y坐标
  • view.getLeft: 按钮左上角相对于父view(LinerLayout)的x坐标
  • 使用layout(),相对于屏幕的坐标,控制view的位置,可以实现很多操作,offsetLeftAndRight(); offsetTopAndBottom();也能实现,父布局刷新的时候,会导致回到原点,所以注意使用

 

 

在做一个view背景特效的时候被坐标的各个获取方法搞晕了,几篇抄来抄去的博客也没弄很清楚。

现在把整个总结一下。

其实只要把下面这张图看明白就没问题了。

android事件处理机制-自定义view

涉及到的方法一共有下面几个:

view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()

view获取自身宽高:getHeight(),getWidth()

motionEvent获取坐标:getX(),getY(),getRawX(),getRawY()

 

首先是view的几个方法,

获取自身的宽高的这两个方法很清楚,不用多说,获取坐标的这几个就有点混乱了。

根据上面的图应该会比较容易明白,图中屏幕上放了一个ViewGroup布局,里面有个View控件

getTop:获取到的,是view自身的顶边到其父布局顶边的距离

getLeft:获取到的,是view自身的左边到其父布局左边的距离

getRight:获取到的,是view自身的右边到其父布局左边的距离

getBottom:获取到的,是view自身的底边到其父布局顶边的距离

 

这些方法获取到的数据可以用在什么地方呢?
比如要实现一个自定义的特殊布局,像http://blog.csdn.net/singwhatiwanna/article/details/42614953
这里要实现的是一个水波纹特效布局,该布局内的任何控件点击后都会出现波纹效果

那么在点击了布局内的一个控件之后,就要通过不断刷新布局,去更新这个控件上面的波纹半径,为了节省资源,每次刷新布局都时候不会整个布局都刷新,而只是通过

 

 

[java] view plain copy

  1. postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);    

 

在布局的画布上每次只去更新点击事件所点击的对应的控件的位置,那么这里就可以用view的那四个方法,分别获取自身的四条边对应的坐标

从而让布局去刷新重绘。

当然博客中是使用绝对坐标去计算的,因为这里实现的是一个布局,可能里面还会嵌套另外的布局,经过多次嵌套之后所获取到的值,是相对于控件直接对应的父布局(这个布局有可能已经是我们重写的布局的子布局了)的距离,这样去刷新的区域肯定是不准确的,所以博客里面使用相对屏幕的绝对坐标计算需要刷新的控件区域。

如果这里自定义的不是布局,而只是一个控件的话,就可以通过以上方法获取到坐标,然后要求自己所在的布局去重绘这一区域就可以了。当然这只是一种思路,其实没必要去要求布局重绘,完全可以直接view自身重绘就可以了。

 

然后是motionEvent的方法:

getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离

getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离

getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离

getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离

这些方法可以用在什么地方呢?

getRawX和getRawY在之前那篇博客里广泛使用了,可以去那里看用法,getX()和getY()这两个方法在对view进行自定义的时候可能用的会比较多。

之后有篇博客写开头实现的特效,可以看下,下面是链接。

 

 

 

 

 

 

 

 

跟touch事件相关的3个方法:

public boolean dispatchTouchEvent(MotionEvent ev);    //用来分派event

public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event 只有ViewGroup有此方法

public boolean onTouchEvent(MotionEvent ev);          //用来处理event

 

拥有这三个方法的类:

Activity类: Activity dispatchTouchEvent();
onTouchEvent();
View容器(ViewGroup的子类): FrameLayout、LinearLayout……
ListView、ScrollVIew……
dispatchTouchEvent();
onInterceptTouchEvent();
onTouchEvent();
View控件(非ViewGroup子类): Button、TextView、EditText…… dispatchTouchEvent();
onTouchEvent();

 

个方法的用法:

dispatchTouchEvent() 用来分派事件。
其中调用了onInterceptTouchEvent()和onTouchEvent(),
onInterceptTouchEvent() 用来拦截事件。
ViewGroup类中的源码实现就是{return false;}表示不拦截该事件,
事件将向下传递(传递给其子View);
若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递,
事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法
onTouchEvent() 用来处理事件。
返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View);
返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理

【注】:ViewGroup的某些子类(GridView、ScrollView...)重写了onInterceptTouchEvent()方法,当发生ACTION_MOVE事件时,返回true进行拦截。

 

为了演示,重写了4个类:

总统 --> MyActivity

省长 --> MyFrameLayout

市长 --> MyLinearLayout

农民 --> MyTextView

android事件处理机制-自定义view

 

【举个通俗易懂的例子】:

总统对省长说:我要吃红烧鱼

省长对市长说:你做个红烧鱼

市长对县长说:你做个红烧鱼

县长对农民说:你做个红烧鱼

   ……(农民做呀做,没做出来)

农民说:我尽力了,但真心不会做呀,饶了我吧

县长说:你个笨蛋,下次不找你了,看我来做

   ……(县长做呀做,没做出来)

县长对市长说:我尽力了,非常抱歉,我不会做

市长说:你个废物,要你何用,只能我自己来做了

   ……(市长做呀做,做成功了)

市长对省长说:红烧鱼做好了

省长说:不错,下次有事还找你

省长对总统说:红烧鱼做好了

总统说:不错,下次有事还找你

---------------------------

总统对省长说:我要吃水煮鱼

省长对市长说:你做个水煮鱼

市长说:县长连红烧鱼都搞不定,这次就不找他了,我自己亲自来做

 ……(市长做呀做,又成功了)

市长对省长说:水煮鱼做好了

省长说:不错,下次有事还找你

省长对总统说:水煮鱼做好了

总统说:不错,下次有事还找你

---------------------------

  • 按常理,领导都会把任务向下分派,一旦下面的人把事情做不好,就不会再把后续的任务交给下面的人来做了,只能自己亲自做,如果自己也做不了,就只能告诉上级不能完成任务,上级又会重复他的过程。
  • 另外,领导都有权利拦截任务,对下级隐瞒该任务,而直接自己去做,如果做不成,也只能向上级报告不能完成任务。

【1】TextView的clickable属性默认是false,所以TextView的onTouchEvent()方法默认返回false,程序输出如下:

android事件处理机制-自定义view

     事件传递示意图:

android事件处理机制-自定义view

 

【2】把TextView的clickable属性手动改成true,或者直接重写onTouchEvent()方法,使其返回true,程序输出如下:

android事件处理机制-自定义view

      事件传递示意图:

android事件处理机制-自定义view

 

【3】手动重写LinearLayout的onInterceptTouchEvent()方法,使其返回true,拦截事件,再重写onTouchEvent()方法,返回true,程序输出:

android事件处理机制-自定义view

      事件传递示意图:

android事件处理机制-自定义view

 

(1)这一系列的传递流程都是dispatchTouchEvent()方法来控制的,如果不人为地干预,事件将由上自下依次传递(因为默认是返回false不会拦截的),传递到最底层的View,就由它的onTouchEvent()方法来处理事件,若处理成功返回true,若处理失败返回false,事件依次向上传递,每个View都调用自己的onTouchEvent()方法来处理事件,若处理成功就终止传递,若处理失败就继续向上传递。

(2)经过人为的干预,若在向下传递的过程中被拦截了,即onInterceptTouchEvent()方法返回true,则事件将停止向下传递,直接由当前的onTouchEvent()方法来处理,若处理成功则OK,若处理不成功,则事件会向上传递。

(3)另外,dispatchTouchEvent()方法中还有“记忆”的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那就说明下面的View都没能成功处理该事件);当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,该View直接调用自己的onTouchEvent()方法来处理该事件。

(4)“记忆”功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。

Demo源码下载:http://download.csdn.net/detail/morgan_xww/5781199

[java] view plain copy  print?

  1. public class MyActivity extends Activity {  
  2.       
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.main);  
  7.     }  
  8.       
  9.     @Override  
  10.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  11.         Log.d("d""【总统】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派");  
  12.         return super.dispatchTouchEvent(ev);  
  13.     }  
  14.       
  15.     @Override  
  16.     public boolean onTouchEvent(MotionEvent ev) {  
  17.         boolean bo = false;  
  18.         Log.d("d""【总统】任务<" + Util.actionToString(ev.getAction()) + "> : 下面都解决不了,下次再也不能靠你们了,哼…只能自己尝试一下啦。能解决?" + bo);  
  19.         return bo;  
  20.     }  
  21. }  

 

[java] view plain copy  print?

  1. public class MyFrameLayout extends FrameLayout  
  2. {  
  3.     public MyFrameLayout(Context context, AttributeSet attrs){  
  4.         super(context, attrs);  
  5.     }  
  6.       
  7.     @Override  
  8.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  9.         Log.d("d""【省长】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派");  
  10.         return super.dispatchTouchEvent(ev);  
  11.     }  
  12.   
  13.     @Override  
  14.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  15.         boolean bo = false;  
  16.         Log.d("d""【省长】任务<" + Util.actionToString(ev.getAction()) + "> : 拦截吗?" + bo);  
  17.         return bo;  
  18.     }  
  19.   
  20.     @Override  
  21.     public boolean onTouchEvent(MotionEvent ev) {  
  22.         boolean bo = false;  
  23.         Log.d("d""【省长】任务<" + Util.actionToString(ev.getAction()) + "> : 市长是个废物,下次再也不找你了,我自己来尝试一下。能解决?" + bo);  
  24.         return bo;  
  25.     }  
  26. }  

 

[java] view plain copy  print?

  1. public class MyLinearLayout extends LinearLayout{  
  2.       
  3.     public MyLinearLayout(Context context, AttributeSet attrs) {  
  4.         super(context, attrs);  
  5.     }  
  6.       
  7.     @Override  
  8.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  9.         Log.d("d""【市长】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派");  
  10.         return super.dispatchTouchEvent(ev);  
  11.     }  
  12.   
  13.     @Override  
  14.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  15.         boolean bo = false;  
  16.         Log.d("d""【市长】任务<" + Util.actionToString(ev.getAction()) + "> : 拦截吗?" + bo);  
  17.         return bo;  
  18.     }  
  19.   
  20.     @Override  
  21.     public boolean onTouchEvent(MotionEvent ev) {  
  22.         boolean bo = false;  
  23.         Log.d("d""【市长】任务<" + Util.actionToString(ev.getAction()) + "> : 农民真没用,下次再也不找你了,我自己来尝试一下。能解决?" + bo);  
  24.         return bo;  
  25.     }  
  26. }  

 

[java] view plain copy  print?

  1. public class MyTextView extends TextView  
  2. {  
  3.     public MyTextView(Context context, AttributeSet attrs){  
  4.         super(context, attrs);  
  5.     }  
  6.       
  7.     @Override  
  8.     public boolean dispatchTouchEvent(MotionEvent ev){  
  9.         Log.d("d""【农民】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派,我下面没人了,怎么办?自己干吧");  
  10.         return super.dispatchTouchEvent(ev);  
  11.     }  
  12.       
  13.     @Override  
  14.     public boolean onTouchEvent(MotionEvent ev){  
  15.         boolean bo = true;  
  16.         Log.d("d""【农民】任务<" + Util.actionToString(ev.getAction()) + "> : 自己动手,埋头苦干。能解决?" + bo);  
  17.         return bo;  
  18.     }  
  19. }