View和ViewGroup
Android的UI界面都是由View和ViewGroup及其派生类组合而成的。其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的,也就是说ViewGroup的父类就是View。
我们常见的UI控件Button,TextView,ImageView等控件都是继承父类View来实现的。RelativeLayout、LinearLayout、FrameLayout等布局都是继承父类ViewGroup来实现的。
事件传递机制
并且View只有dispatchTouchEvent()和onTouchEvent();
ViewGroup有dispatchTouchEvent(),onInterceptTouchEvent()和onTouchEvent();
为了验证时间传递过程,我自定义了一个布局(ViewGroup),里面放一个自定义控件(View),直接上代码:
Activity类:
/**
* Created by zjp on 2016/5/12 09:53.
*/
public class Test2Activity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test2_layout);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("Activity---dispatchTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("Activity---dispatchTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("Activity---dispatchTouchEvent---UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("Activity---onTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("Activity---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("Activity---onTouchEvent---UP");
break;
}
return super.onTouchEvent(event);
}
}
自定义CustomLayout类:
/**
* Created by zjp on 2016/5/12 09:48.
*/
public class CustomLayout extends RelativeLayout {
@Override
public CustomLayout(Context context) {
this(context, null);
}
@Override
public CustomLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@Override
public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("CustomLayout---dispatchTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("CustomLayout---dispatchTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("CustomLayout---dispatchTouchEvent---UP");
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 因为ViewGroup有onInterceptTouchEvent()方法,所以我对他进行重写
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println("CustomLayout--- onInterceptTouchEvent---");
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("CustomLayout---onTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("CustomLayout---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("CustomLayout---onTouchEvent---UP");
break;
}
return super.onTouchEvent(event);
}
}
自定义CustomView类:
/**
* Created by zjp on 2016/5/12 09:57.
*/
public class CustomView extends Button {
@Override
public CustomView (Context context) {
this(context, null);
}
@Override
public CustomView (Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@Override
public CustomView (Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("CustomView---dispatchTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("CustomView---dispatchTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("CustomView---dispatchTouchEvent---UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("CustomView---onTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("CustomView---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("CustomView---onTouchEvent---UP");
break;
}
return super.onTouchEvent(event);
}
}
接下来是布局了
Activity的布局:test2_layout.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_purple"
android:orientation="vertical">
<app.application.view.test2.CustomLayout
android:layout_width="300dp"
android:layout_height="400dp"
android:background="@android:color/holo_blue_bright">
<app.application.view.test2.CustomView
android:layout_width="200dp"
android:layout_height="200dp"
android:text="我是Button"
android:gravity="center"
android:textColor="@color/bg_white"
android:background="@android:color/holo_red_dark" />
</app.application.view.test2.CustomLayout>
</LinearLayout>
效果图:
当我们点击Button的时候,log日志如下:
你会发现,事件从Activity传递到子View,再从子View返回到Activity:
①从Activity传递到子View:
Activity:dispatchTouchEvent --> CustomLayout:dispatchTouchEvent --> CustomLayout:onInterceptTouchEvent --> CustomView:dispatchTouchEvent
②从子View传递到Activity:
CustomView:onTouchEvent --> CustomLayout :onTouchEvent--> Activity: onTouchEvent
细心的朋友可能会发现,自定义布局中onInterceptTouchEvent()返回值是false。
如果我把值改为true。又是什么情况呢?
①从Activity传递到子View:
Activity:dispatchTouchEvent --> CustomLayout:dispatchTouchEvent --> CustomLayout:onInterceptTouchEvent
②从子View传递到Activity:
CustomLayout :onTouchEvent--> Activity: onTouchEvent
你会发现,子View的事件没走,这是因为,事件从Activity传递到 CustomLayout 的onInterceptTouchEvent事件中,由于onInterceptTouchEvent返回true,被自身消费了,就不再往下传递。
先来看整体的事件传递过程:
L:LinearLayout R:RealativeLayout B:Button
当手指点击按钮B时,事件传递的顺序是从底向上传递的,也就是按照L->R->B的顺序由下往上逐层传递,响应正好相反,是自上而下。
L首先接收到点击事件,L的父类是ViewGroup类,并将事件传递给dispatchTouchEvent方法,dispatchTouchEvent函数中判断该控件L是否重载了onInterceptTouchEvent方法进行事件拦截,onInterceptTouchEvent默认返回false不拦截,那么dispatchTouchEvent方法将事件传递给R去处理(进入第2流程处理),如果返回true表示当前L控件拦截了事件向其它控件的传递,交给它自己父类View的dispatchTouchEvent去处理,在父方法的dispatchTouchEvent中,将会按照前面讲的View的事件处理机制去判断,比如判断L是否重载了onTouch方法,是否可点击,是否做了监听等事件。
R也是ViewGroup的子类,因此与第1流程基本相似,如果onInterceptTouchEvent返回了false,表示事件将不拦截继续传递给B。
B是View的子类,它没有onInterceptTouchEvent方法,直接交给自己父类View的dispatchTouchEvent去处理,流程同不再敷述。
今天手写了一个图,更容易 理解