本文為自己多年來在Android實戰(zhàn)開發(fā)過程中總結歸納的一些常見問題波势,現(xiàn)在分享出來希望對初學者有所幫助翎朱。
本文出自門心叼龍的博客,轉載請注明出處: https://blog.csdn.net/geduo_83/article/details/86560896
事件分發(fā)是Android開發(fā)過程中的重點又是難點艰亮, 一張事件分發(fā)流程圖闭翩,讓你徹底搞明白。網(wǎng)上有很多文章寫事件分發(fā)迄埃,感覺都沒有講明白疗韵,恭喜你,今天你看到好文章了侄非,你徹底搞清楚...
目錄
[1.在Android操作系統(tǒng)中蕉汪,擁有事件傳遞功能的類都有哪些?]
[2.觸摸事件的類型逞怨?]
[3.事件傳遞的三個階段者疤?]
[4.簡述View的事件傳遞機制?]
[5.簡述ViewGroup的事件傳遞機制?]
[6.事件分發(fā)流程圖]
[7.實戰(zhàn)案例]
1.在Android操作系統(tǒng)中叠赦,擁有事件傳遞功能的類都有哪些驹马?
- Activity:擁有dispathTouchEvent和onTouchEvent方法
- View:擁有dispathTouchEvent和onTouchEvent方法
- ViewGroup:擁有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
2.觸摸事件的類型糯累?
主要有三種:
- ACTION_DOWN : 手指的按下操作
- ACTION_MOVE:手指按下后算利,松開手之前,輕微移動所觸發(fā)的事件
- ACTION_UP:手指離開屏幕的操作
3.事件傳遞的三個階段泳姐?
-
3.1 按照事件進行劃分
- 3.2 按照View進行劃分
結論:無論是View還是ViewGroup效拭,不管他是DispatchTouchEvent還是onTouchEvent方法,方法返回true胖秒、返回false的處理邏輯都是一樣的缎患,只是調(diào)用父類的同名方法的時候處理的邏輯有所不同,View偏重消費阎肝、ViewGourp偏重分發(fā)
4.簡述View的事件傳遞機制挤渔?
觸摸事件的傳遞流程是從dispatchTouchEvent開始的,如果不進行人工干預盗痒,則事件將會依照View樹的嵌套層次從外層向內(nèi)層傳遞蚂蕴,到達最內(nèi)層的View時低散,就由它的onTouchVent方法處理
如果事件在傳遞過程中俯邓,進行了人工干預,事件分發(fā)函數(shù)返回true表示自行消費熔号,返回父類的同名方法則該事件傳遞給自身的onTouchEvent進行處理稽鞭,返回false表示該事件會回傳給父view的onTouchEvent方法進行處理,此時后面的事件都接受不到了引镊,最后由哪個View處理朦蕴,以后的所有事件都交由它來處理
如果事件在傳遞過程中,進行了人工干預弟头,事件處理函數(shù)吩抓,返回true和調(diào)用同名方法表示該事件被消費,返回false則表示該事件回傳給父類的同名方法進行處理
事件觸發(fā)是先觸發(fā)onTouch赴恨,再觸發(fā)onClick疹娶,如果onTouch方法返回tue,表示消費掉該事件伦连,不在繼續(xù)進行事件傳遞雨饺,onClick也不會被調(diào)用,如果onTouch方法返回false惑淳,則繼續(xù)會事件傳遞额港,onClick會被調(diào)用
5.簡述ViewGroup的事件傳遞機制?
觸摸事件的傳遞順序是由Activity到ViewGroup,再由ViewGroup遞歸傳遞給他的子View歧焦,ViewGroup通過onInterceptTouchEvent方法對事件進行攔截移斩,如果該方法返回true,則事件不會繼續(xù)往下傳遞給子View,如果返回false或者是調(diào)用super.onInterceptTouchEvent,則事件會繼續(xù)會傳遞給子View
如果事件在傳遞過程中向瓷,進行了人工干預忍宋,事件分發(fā)函數(shù)返回true表示事件被自行消費,返回false风罩,則回傳給父View的onTouchEvent進行處理糠排,此時后面的事件都接受不到了,調(diào)用同名方法則繼續(xù)傳遞
如果事件在傳遞過程中超升,進行了人工干預入宦,事件處理函數(shù),返回true則表示該事件被消費室琢,返回false和調(diào)用同名方法則表示該事件回傳給父類的同名方法進行處理
6.事件分發(fā)流程圖
一張android事件分發(fā)流程乾闰,讓你徹底搞明白Android的事件分發(fā)機制
ViewGroup的事件分發(fā)的偽代碼:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
if (!(mOnTouchListener != null && mOnTouchListener.onTouchEvent(ev))) {
consume = onTouchEvent(ev);
}
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
public void onTouchEvent(MotionEvent ev) {
boolean consume = false;
if (mOnClickListener != null) {
mOnClickListener.onClick(this);
consume = true;
}
return consume;
}
View的事件分發(fā)的偽代碼:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
consume = onTouchEvent(ev);
return consume;
}
public void onTouchEvent(MotionEvent ev) {
boolean consume = false;
if (mOnClickListener != null) {
mOnClickListener.onClick(this);
consume = true;
}
return consume;
}
7.實戰(zhàn)案例
實現(xiàn)效果如下,底部的行程詳情可以往上拖動覆蓋在地圖之上盈滴,也可以往下拖動停止在屏幕的正中位置涯肩,地圖相關操作:放大、縮小巢钓、移動都能正常的響應病苗,怎么實現(xiàn)?下面就是具體的源碼實現(xiàn)過程
?
- 7.1 事件分發(fā)處理
mTransparentView = findViewById(R.id.view_tansparent);
mTransparentView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return mMapView.dispatchTouchEvent(event);
}
});
mScrollView = findViewById(R.id.view_scrollview);
mScrollView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Rect rect = new Rect();
mTransparentView.getLocalVisibleRect(rect);
if(rect.contains((int)event.getX(),(int)event.getY())){
return true;
}else{
return false;
}
}
});
- 7.2 自定義ViewTransparentView
public class TransparentView extends View {
TouchEventListener mListener;
public interface TouchEventListener{
boolean dispatchTouchEvent(MotionEvent event);
}
public TransparentView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(mListener != null){
return mListener.dispatchTouchEvent(event);
}else{
return super.dispatchTouchEvent(event);
}
}
public void setListener(TouchEventListener listener) {
mListener = listener;
}
}
- 7.3 自定義TransScrollView
public class TransScrollView extends NestedScrollView {
public TransparentView.TouchEventListener mListener;
public TransScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mListener != null && mListener.dispatchTouchEvent(ev)) {
return false;
}
return super.onInterceptTouchEvent(ev);
}
public void setListener(TransparentView.TouchEventListener listener) {
mListener = listener;
}
}
- 7.4 布局文件
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="250dp"
/>
<com.zhijiaxing.travel.trip.record.view.TransScrollView
android:id="@+id/view_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<com.zhijiaxing.travel.trip.record.view.TransparentView
android:id="@+id/view_tansparent"
android:layout_width="match_parent"
android:layout_height="250dp"
android:background="#00000000"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:orientation="vertical"
>
</LinearLayout>
</LinearLayout>
</com.zhijiaxing.travel.trip.record.view.TransScrollView>
</FrameLayout>