Android初级开发笔记 -- Android事件分发

2019-10-233903

之前写过一篇浅谈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);
    }
});

那么返回的是接口方法的返回值。至此,点击事件传给了地图控件处理。

分享交流才能更好成长,如果文章有任何错误之处,恳请各位看官不吝赐教,万分感激~

分享
点赞2
打赏
上一篇:Jenkins + GitLab + Xcode + Fir 实现iOS自动打包和分发
下一篇:Android初级开发笔记 -- 关于Fragment的回退栈