之前寫過一篇淺談Android事件分發(fā)。有興趣的旁友可猛戳-〉淺談Android事件分發(fā)。
今日主要是寫一下實(shí)戰(zhàn)例子。下面這個(gè)例子是摘抄了Android事件分發(fā)機(jī)制詳解與實(shí)戰(zhàn)剖析,一張事件分發(fā)流程圖,讓你徹底搞明白的實(shí)例。
實(shí)現(xiàn)效果如下,底部的行程詳情可以往上拖動(dòng)覆蓋在地圖之上,也可以往下拖動(dòng)停止在屏幕的正中位置,地圖相關(guān)操作: 放大、縮小、移動(dòng)都能正常的響應(yīng),怎么實(shí)現(xiàn)?

針對(duì)上面的例子,我們來看一下源碼。
一:布局文件
<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"
/>
//占據(jù)整個(gè)頁面
<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>
我們可以知道FrameLayout包含的是地圖組件和自定義的TransScrollView。
FrameLayout是層疊的視圖,下一個(gè)組件會(huì)疊加在上一個(gè)上方。左頂點(diǎn)為左上角。
TransScrollView里面包含的是一個(gè)LinearLayout1,
LinearLayout1里面包含了自定義的TransparentView和一個(gè)包含了各種組件的LinearLayout2。
TransparentView跟地圖組件一樣高,位于頂端,也就是和地圖同個(gè)位置(未滑動(dòng)時(shí))只是ta是透明的。
二:自定義 TransparentView
public class TransparentView extends View {
TouchEventListener mListener;
//定義TouchEventListener接口
public interface TouchEventListener{
boolean dispatchTouchEvent(MotionEvent event);
}
public TransparentView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
//重寫父類的dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(mListener != null){
return mListener.dispatchTouchEvent(event);
}else{
return super.dispatchTouchEvent(event);
}
}
//通過setListener()初始化mListener
public void setListener(TouchEventListener listener) {
mListener = listener;
}
}
三:自定義 TransScrollView
public class TransScrollView extends NestedScrollView {
public TransparentView.TouchEventListener mListener;
public TransScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
//重寫ViewGroup中特有的onInterceptTouchEvent,設(shè)置根據(jù)接口實(shí)現(xiàn)方法返回值進(jìn)行返回
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mListener != null && mListener.dispatchTouchEvent(ev)) {
return false;
}
return super.onInterceptTouchEvent(ev);
}
//通過setListener()初始化mListener
public void setListener(TransparentView.TouchEventListener listener) {
mListener = listener;
}
}
四:事件分發(fā)處理
mTransparentView = findViewById(R.id.view_tansparent);
//實(shí)現(xiàn)TouchEventListener的dispatchTouchEvent方法,將點(diǎn)擊事件傳給地圖控件進(jìn)行處理
mTransparentView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return mMapView.dispatchTouchEvent(event);
}
});
mScrollView = findViewById(R.id.view_scrollview);
//實(shí)現(xiàn)TouchEventListener的dispatchTouchEvent方法,
//若點(diǎn)擊位置在TransparentView區(qū)域,那么返回true
mScrollView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Rect rect = new Rect();
//獲取TransparentView矩形范圍
mTransparentView.getLocalVisibleRect(rect);
//boolean contains(int x, int y)是否包含(x,y)點(diǎn)
//Returns true if (x,y) is inside the rectangle.
//判斷點(diǎn)擊處是否在這個(gè)矩形內(nèi)
if(rect.contains((int)event.getX(),(int)event.getY())){
return true;
}else{
return false;
}
}
});
來~總結(jié)一下:
該例子,布局為兩層,三大塊,一塊是地圖,一塊是TransparentView,一塊是LinearLayout2包含的各種組件view。
那么在點(diǎn)擊到地圖的時(shí)候,事件自然給其處理。點(diǎn)擊到TransparentView也應(yīng)該是給地圖,所以在TouchEventListener接口的dispatchTouchEvent中進(jìn)行了處理。點(diǎn)擊到LinearLayout2,根據(jù)情況判斷是否攔截,或者傳給不同的控件進(jìn)行處理。
還不明白?那么再重點(diǎn)解釋一下如何實(shí)現(xiàn)TransparentView的事件最后交給了地圖控件處理。
首先當(dāng)我們點(diǎn)擊了TransparentView的時(shí)候,【TransparentView [View] 是包含在TransScrollView [ViewGroup] 里面的】
(1)執(zhí)行TransScrollView的dispatchTouchEvent方法,沒有重載,好的默認(rèn)返回。
(2)接著是TransScrollView的onInterceptTouchEvent方法,該方法被重載。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mListener != null && mListener.dispatchTouchEvent(ev)) {
return false;
}
return super.onInterceptTouchEvent(ev);
}
根據(jù)是否實(shí)現(xiàn)了TouchEventListener,以及該接口方法的返回值進(jìn)行返回。由于實(shí)現(xiàn)了,
mScrollView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Rect rect = new Rect();
//獲取TransparentView矩形范圍
mTransparentView.getLocalVisibleRect(rect);
//boolean contains(int x, int y)是否包含(x,y)點(diǎn)
//Returns true if (x,y) is inside the rectangle.
//判斷點(diǎn)擊處是否在這個(gè)矩形內(nèi)
if(rect.contains((int)event.getX(),(int)event.getY())){
return true;
}else{
return false;
}
}
});
而且因?yàn)辄c(diǎn)擊的是TransparentView的矩形區(qū)域,所以該接口方法返回true。
【注意:上面那段代碼的的dispatchTouchEvent不是事件分發(fā)中的dispatchTouchEvent,而是接口定義的dispatchTouchEvent】
那么TransScrollView的onInterceptTouchEven返回的是false,不攔截。
(3)好嘞~接下來事件傳給了TransparentView的dispatchTouchEvent,該方法被重載。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(mListener != null){
return mListener.dispatchTouchEvent(event);
}else{
return super.dispatchTouchEvent(event);
}
}
TransparentView也實(shí)現(xiàn)了TouchEventListener接口,
mTransparentView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return mMapView.dispatchTouchEvent(event);
}
});
那么返回的是接口方法的返回值。至此,點(diǎn)擊事件傳給了地圖控件處理。
分享交流才能更好成長,如果文章有任何錯(cuò)誤之處,懇請(qǐng)各位看官不吝賜教,萬分感激~
內(nèi)推信息
- 我們正在招募小伙伴,有興趣的小伙伴可以把簡歷發(fā)到 app@talkmoney.cn,備注:來自簡書社區(qū)
- 詳情可以戳這里--> 廣州蘆葦信息科技