View和ViewGroup
Android的UI界面都是由View和ViewGroup及其派生類組合而成的试吁。其中纯丸,View是所有UI組件的基類侈百,而ViewGroup是容納這些組件的容器滨彻,其本身也是從View派生出來(lái)的鸳谜,也就是說ViewGroup的父類就是View。
我們常見的UI控件Button嚣艇,TextView承冰,ImageView等控件都是繼承父類View來(lái)實(shí)現(xiàn)的。RelativeLayout食零、LinearLayout困乒、FrameLayout等布局都是繼承父類ViewGroup來(lái)實(shí)現(xiàn)的。
事件傳遞機(jī)制
并且View只有dispatchTouchEvent()和onTouchEvent()贰谣;
ViewGroup有dispatchTouchEvent()娜搂,onInterceptTouchEvent()和onTouchEvent();
為了驗(yàn)證時(shí)間傳遞過程迁霎,我自定義了一個(gè)布局(ViewGroup),里面放一個(gè)自定義控件(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);
}
/**
* 因?yàn)閂iewGroup有onInterceptTouchEvent()方法欧引,所以我對(duì)他進(jìn)行重寫
*/
@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);
}
}
接下來(lái)是布局了
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>
效果圖:
當(dāng)我們點(diǎn)擊Button的時(shí)候,log日志如下:
你會(huì)發(fā)現(xiàn)恳谎,事件從Activity傳遞到子View,再?gòu)淖覸iew返回到Activity:
①?gòu)腁ctivity傳遞到子View:
Activity:dispatchTouchEvent --> CustomLayout:dispatchTouchEvent --> CustomLayout:onInterceptTouchEvent --> CustomView:dispatchTouchEvent
②從子View傳遞到Activity:
CustomView:onTouchEvent --> CustomLayout :onTouchEvent--> Activity: onTouchEvent
細(xì)心的朋友可能會(huì)發(fā)現(xiàn)憋肖,自定義布局中onInterceptTouchEvent()返回值是false因痛。
如果我把值改為true。又是什么情況呢岸更?
①?gòu)腁ctivity傳遞到子View:
Activity:dispatchTouchEvent --> CustomLayout:dispatchTouchEvent --> CustomLayout:onInterceptTouchEvent
②從子View傳遞到Activity:
CustomLayout :onTouchEvent--> Activity: onTouchEvent
你會(huì)發(fā)現(xiàn)鸵膏,子View的事件沒走,這是因?yàn)樵醮叮录腁ctivity傳遞到 CustomLayout 的onInterceptTouchEvent事件中谭企,由于onInterceptTouchEvent返回true,被自身消費(fèi)了评肆,就不再往下傳遞债查。
先來(lái)看整體的事件傳遞過程:
L:LinearLayout R:RealativeLayout B:Button
當(dāng)手指點(diǎn)擊按鈕B時(shí),事件傳遞的順序是從底向上傳遞的瓜挽,也就是按照L->R->B的順序由下往上逐層傳遞盹廷,響應(yīng)正好相反,是自上而下久橙。
L首先接收到點(diǎn)擊事件俄占,L的父類是ViewGroup類,并將事件傳遞給dispatchTouchEvent方法淆衷,dispatchTouchEvent函數(shù)中判斷該控件L是否重載了onInterceptTouchEvent方法進(jìn)行事件攔截缸榄,onInterceptTouchEvent默認(rèn)返回false不攔截,那么dispatchTouchEvent方法將事件傳遞給R去處理(進(jìn)入第2流程處理)祝拯,如果返回true表示當(dāng)前L控件攔截了事件向其它控件的傳遞甚带,交給它自己父類View的dispatchTouchEvent去處理,在父方法的dispatchTouchEvent中鹿驼,將會(huì)按照前面講的View的事件處理機(jī)制去判斷欲低,比如判斷L是否重載了onTouch方法,是否可點(diǎn)擊畜晰,是否做了監(jiān)聽等事件砾莱。
R也是ViewGroup的子類,因此與第1流程基本相似凄鼻,如果onInterceptTouchEvent返回了false腊瑟,表示事件將不攔截繼續(xù)傳遞給B聚假。
B是View的子類,它沒有onInterceptTouchEvent方法闰非,直接交給自己父類View的dispatchTouchEvent去處理膘格,流程同不再敷述。
今天手寫了一個(gè)圖财松,更容易 理解