之前写过一篇浅谈Android事件分发。有兴趣的旁友可猛戳-〉浅谈Android事件分发。
今日主要是写一下实战例子。下面这个例子是摘抄了Android事件分发机制详解与实战剖析,一张事件分发流程图,让你彻底搞明白的实例。
实现效果如下,底部的行程详情可以往上拖动覆盖在地图之上,也可以往下拖动停止在屏幕的正中位置,地图相关操作: 放大、缩小、移动都能正常的响应,怎么实现? 针对上面的例子,我们来看一下源码。
一:布局文件
<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>
我们可以知道FrameLayout包含的是地图组件和自定义的TransScrollView。
FrameLayout是层叠的视图,下一个组件会叠加在上一个上方。左顶点为左上角。
TransScrollView里面包含的是一个LinearLayout1,
LinearLayout1里面包含了自定义的TransparentView和一个包含了各种组件的LinearLayout2。
TransparentView跟地图组件一样高,位于顶端,也就是和地图同个位置(未滑动时)只是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,设置根据接口实现方法返回值进行返回
@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;
}
}
四:事件分发处理
mTransparentView = findViewById(R.id.view_tansparent);
//实现TouchEventListener的dispatchTouchEvent方法,将点击事件传给地图控件进行处理
mTransparentView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return mMapView.dispatchTouchEvent(event);
}
});
mScrollView = findViewById(R.id.view_scrollview);
//实现TouchEventListener的dispatchTouchEvent方法,
//若点击位置在TransparentView区域,那么返回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)点
//Returns true if (x,y) is inside the rectangle.
//判断点击处是否在这个矩形内
if(rect.contains((int)event.getX(),(int)event.getY())){
return true;
}else{
return false;
}
}
});
来~总结一下: 该例子,布局为两层,三大块,一块是地图,一块是TransparentView,一块是LinearLayout2包含的各种组件view。
那么在点击到地图的时候,事件自然给其处理。点击到TransparentView也应该是给地图,所以在TouchEventListener接口的dispatchTouchEvent中进行了处理。点击到LinearLayout2,根据情况判断是否拦截,或者传给不同的控件进行处理。
还不明白?那么再重点解释一下如何实现TransparentView的事件最后交给了地图控件处理。
首先当我们点击了TransparentView的时候,【TransparentView [View] 是包含在TransScrollView [ViewGroup] 里面的】
(1)执行TransScrollView的dispatchTouchEvent方法,没有重载,好的默认返回。
(2)接着是TransScrollView的onInterceptTouchEvent方法,该方法被重载。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mListener != null && mListener.dispatchTouchEvent(ev)) {
return false;
}
return super.onInterceptTouchEvent(ev);
}
根据是否实现了TouchEventListener,以及该接口方法的返回值进行返回。由于实现了,
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)点
//Returns true if (x,y) is inside the rectangle.
//判断点击处是否在这个矩形内
if(rect.contains((int)event.getX(),(int)event.getY())){
return true;
}else{
return false;
}
}
});
而且因为点击的是TransparentView的矩形区域,所以该接口方法返回true。
【注意:上面那段代码的的dispatchTouchEvent不是事件分发中的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也实现了TouchEventListener接口,
mTransparentView.setListener(new TransparentView.TouchEventListener() {
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return mMapView.dispatchTouchEvent(event);
}
});
那么返回的是接口方法的返回值。至此,点击事件传给了地图控件处理。
分享交流才能更好成长,如果文章有任何错误之处,恳请各位看官不吝赐教,万分感激~