android自定义控件系列教程-----touch事件的传递

前沿:

很久没有写过博客了,因为工作的原因很少有时间写东西了,最近想写一个UI系列的博客,因为我发现这一系列的都很少,而且没有那么 系统 ,这里我想以我自己的观点来阐述一下如何自定义android 控件系列。

自定义控件阐述:

在我的理解里面自定义控件,需要了解到touch事件的传递、分发、拦截机制,Scroller类的运用,andorid 视图的理解,ViewGroup的熟悉,因为我们绝大多的控件都是继承自ViewGroup,还有就是要学会布局测量等。

Touch事件的传递

首先我们要了解在android系统里面有几个地方会走touch事件,这个是老生常谈的问题了,但是我还是希望写一下这个问题,因为温故而知新嘛,我们首先得知道VIew类这种不能作为容器的类只会有这两个函数:
?
1
2
3
4
5
6
7
8
9
10
11
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
     // TODO Auto-generated method stub
     return super .dispatchTouchEvent(event);
}
 
@Override
public boolean onTouchEvent(MotionEvent event) {
     // TODO Auto-generated method stub
     return super .onTouchEvent(event);
}
而能做为容器的类如ViewGroup以及继承它的类会有这几个函数:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // TODO Auto-generated method stub
         return super .onInterceptTouchEvent(ev);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // TODO Auto-generated method stub
         return super .onTouchEvent(event);
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         // TODO Auto-generated method stub
         return super .dispatchTouchEvent(ev);
     }
现在我们就分别来重写这几个函数分别走一遍流程,让我们更能清楚的看到android的touch事件是怎么传递的。我们来重写一下这两类的方法。重写一个TextView
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class MyTextView extends TextView{
     private final String TAG = MyTextView. class .getSimpleName();
 
     public MyTextView(Context context) {
         super (context);
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent event) {
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_DOWN:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_UP:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_UP" );
             break ;
         case MotionEvent.ACTION_MOVE:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_MOVE" );
             break ;
         case MotionEvent.ACTION_UP:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_UP" );
             break ;
         }
         return super .dispatchTouchEvent(event);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_DOWN:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_UP:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_UP" );
             break ;
         case MotionEvent.ACTION_MOVE:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_MOVE" );
             break ;
         case MotionEvent.ACTION_UP:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_UP" );
             break ;
         }
         return super .onTouchEvent(event);
     }
}
然后我们再重写一个LinearLayout
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class MyLinearLayout extends LinearLayout {
     private final String TAG = MyLinearLayout. class .getSimpleName();
 
     public MyLinearLayout(Context context) {
         super (context);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         switch (ev.getAction()) {
         case MotionEvent.ACTION_DOWN:
             Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_DOWN:
             Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_POINTER_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_UP:
             Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_POINTER_UP" );
             break ;
         case MotionEvent.ACTION_MOVE:
             Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_MOVE" );
             break ;
         case MotionEvent.ACTION_UP:
             Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_UP" );
             break ;
         }
         return super .onInterceptTouchEvent(ev);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_DOWN:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_UP:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_UP" );
             break ;
         case MotionEvent.ACTION_MOVE:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_MOVE" );
             break ;
         case MotionEvent.ACTION_UP:
             Log.d(TAG, TAG + "onTouchEvent+ACTION_UP" );
             break ;
         }
         return super .onTouchEvent(event);
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         switch (ev.getAction()) {
         case MotionEvent.ACTION_DOWN:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_DOWN:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_DOWN" );
             break ;
         case MotionEvent.ACTION_POINTER_UP:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_UP" );
             break ;
         case MotionEvent.ACTION_MOVE:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_MOVE" );
             break ;
         case MotionEvent.ACTION_UP:
             Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_UP" );
             break ;
         }
         return super .dispatchTouchEvent(ev);
     }
}


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
     super .onCreate(savedInstanceState);
     MyLinearLayout layout = new MyLinearLayout( this );
     
     
     MyTextView myTextView = new MyTextView( this );
     myTextView.setText( "touch event" );
     
     layout.addView(myTextView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
     
     setContentView(layout);
}
}
我们来看看log  android自定义控件系列教程-----touch事件的传递_第1张图片 然后我们在把TextView改成Button来看一下然后在看一下log  android自定义控件系列教程-----touch事件的传递_第2张图片 分析上面的log我们看到,touch事件首先走到的是父亲的dispatchTouchEvent方法中,然后是onInterceptTouchEvent过后才是MyTextView的dispatchTouchEvent和TouchEent中。所以我们知道了一件事: 那就是所有的touch事件一定是先传递给父亲然后在传递给孩子,然后在分析函数执行的顺序最开始执行的是dispatchTouchEvent方法,后面执行onInterceptTouchEvent最后才走到onTouchEvent事件当中。同时我们也要注意这几个函数的的返回值是boolean变量。然后我们再把MyLinearLayout的dispatchTouchEvent方法的返回值返回true,跑一下。
android自定义控件系列教程-----touch事件的传递_第3张图片
你会看到事件始终都只在MyLinearLayout里面了,根本就没有传递到孩子里面去了。同样我们把它返回false,试试 
\
这时候看到只有down事件了,这说明了什么呢?当dispatchTouchEvent返回true的时候会继续传递事件给自身控件dispatchTouchEvent处理,就像刚才的MylinearLayout一样仍然可以执行,DOWN和UP事件,当我们返回了false就连自身控件都不能接收到事件了。同样我们把dispatchTouchEvent还原,然后在onInterceptTouchEvent里面返回true; 

android自定义控件系列教程-----touch事件的传递_第4张图片  可以看到事件也只分发到自身控件并进入到自身的OntouchEvent中,然后我们在把onInterceptTouchEvent的返回值改为false,看看结果 
android自定义控件系列教程-----touch事件的传递_第5张图片
可以看到我们的事件没有收到任何影响所有的都照常执行,同样把MylinearLayout中的onInterceptTouchEvent复原,现在注意了我们要修改的是MyTextView中的onTouchEvent的返回值,我们把它修改为true 
android自定义控件系列教程-----touch事件的传递_第6张图片
可以看到也没有任何变化,我们在试试把它改为false。 
android自定义控件系列教程-----touch事件的传递_第7张图片
可以看到MytextView只接收到DOWN事件就再也不会接收到其他事件了。

结论:

androidtouch事件的默认传递顺序的父亲控件到子控件,而函数的调用顺序大致是这样的dispatchTouchEvent---->消息分发--->onInterceptTouchEvent--->事件打断-->onTouchEvent-->>事件处理。 关于返回值的问题我来简单的总结一下刚才上面的实验和结论: dispatchTouchEvent返回的是true的时候事件是不会继续往下面传递了,一直都会走本身的dispatchTouchEvent这个方法,当返回为false的时候只会走一次dispatchTouchEvent这个方法的Down事件: onInterceptTouchEvent返回的是true的时候是把事件拦截了让他走自身的onTouchEvent方法,当他返回为false的方法的时候不进行拦截正常进行。 onTouchEvent这个方法的返回值是true的时候,继续分发事件到自身的touchEvent中,当为false的时候只会分发第一次down事件,还有各种返回值的组合我就不一一举例了,要想写好自定义的UI和控件touch事件的分发是必须得学好的,剩下的就慢慢的自己体会把。

你可能感兴趣的:(android,自定义控件)