学习的最终目标就是要学以致用,本文所分享的案例都是自己在公司实战开发过程中的真实案例,现在把它分享出来,希望对初学者有所帮助

版权声明:本文来自门心叼龙的博客,属于原创内容,转载请注明出处:https://blog.csdn.net/geduo_83/article/details/90145083

github源码下载地址:https://github.com/geduo83/android-touch-event
Android事件分发机制的探索与发现之View篇
Android事件分发机制的探索与发现之ViewGroup篇
Android事件分发机制的探索与发现之Activity篇
Android事件分发机制的探索与发现之总结篇
Android事件分发机制在实战开发中的应用之一
Android事件分发机制在实战开发中的应用之二

上一篇我们讲了了两个小案例,想必大家通过这两个小案例的学习,对Android事件分发的认知就更加深刻了,那么这篇文章我们通过一个稍微复杂的案例,来进一步加深对事件分发机制的理解,该案例都是自己在公司实战项目中的总结与归纳,我们先睹为快,看一下效果图:

通过gif动态效果图我们可以看到,整个页面分割为了两部分,上半部分为地图,下半部分为文字内容区RxJava详解和Dagger详解,需求是当我们手指在文字内容区上下滑动的时候,整个文字内容区会跟随手指上下的移动并且它会盖在地图的上面进行滚动,当我们点击缩放按钮的时候,地图会逐渐的放大直到占满整个屏幕的高度,此时文字内容区随着地图的放大会逐渐的消失,当我们再次点击按钮的时候地图会逐渐的缩小直到初始位置,此时文字内容区也会从屏幕底部逐渐移动到初始的位置,缩放按钮可以来回的切换,当我们手指在地图上进行点击,滑动地图会放大,移动等操作。

文字内容区的滚动

我们想了文字内容区能够随着手指上下滑动让我们很容易想到ScrollView控件,当然ScrollView控件已经过时了,我们就用NestedScrollView了来代替他了,用它来包裹我们的地图和文字内容区可行吗?当然不行了因为我们的地图是不需要滚动的,文字内容能活动,地图不能滚动,而且文字内容区还能在在地图上滚动,分析到这儿我们不难下出结论:地图和文字内容区肯定是被相对布局RelativeLayout包裹的,而且还只能是文字内容区盖在地图之上,否则的话用线性布局LinearLayout来包裹,根本就不可能文字内容区在地图上滚动了,那又有问题了,因为刚才分析过文字内容区是盖在地图之上的,那么地图怎么显示出来?有人可能会想到用margin来解决这个问题,如果这样那么文字内容区就只能在地图的下方位置滚动了。怎么办?灵机一动,我在地图之上盖一个和地图同等大小的透明View不就行了吗?是的,这样问题就解决了,此时文字内容区和和地图等大的View就被NestedScrollView包裹起来了,然后NestedScrollView盖在地图之上就ok了。

布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"xmlns:android="http://schemas.android.com/apk/res/android"><android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="?attr/colorPrimary"app:popupTheme="@style/AppTheme.PopupOverlay"/><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/layout_blog_root"android:layout_width="match_parent"android:layout_height="match_parent"><com.android.touchevent.view.BlogMapViewandroid:id="@+id/blog_map_view"android:layout_width="match_parent"android:layout_height="250dp"android:layout_alignParentTop="true"/><com.android.touchevent.view.BlogScrollViewandroid:id="@+id/view_scrollview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignParentTop="true"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><com.android.touchevent.view.BlogTransViewandroid:id="@+id/view_blog_trans"android:layout_width="match_parent"android:layout_height="250dp"android:background="#00000000"/><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/layout_blog_detail"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#ffffff"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="10dp"android:text="RxJava线程切换之subscribeOn和observeOn详解"/></LinearLayout></LinearLayout></com.android.touchevent.view.BlogScrollView></RelativeLayout>
</LinearLayout>

地图事件的响应

分析了这么久我们只解决了第一个问题,文字内容区可以在地图上滚动,但是此时我手指在地图上操作的时候,只是文字内容区在滚动,地图并没有任何响应,为什么?因为表面上看你此时操作的是地图,其实上你操作的是NestedScrollView包裹的透明View,此时你的手势事件,都被NestedScrollView拦截处理了,那么我们怎么办?我们只要自定义一个NestedScrollView在手势滑动的时候,我们在它的事件处理方法里我们判断只要此时是手指是在透明View的操作,我们就把该事件传递给透明View,然后透明View把这个事件传递给地图就ok了。

  • 自定义NestedScrollView:BlogScrollView
public class BlogScrollView extends NestedScrollView {public BlogTransView.OnTouchEventListener mTouchEventListener;public BlogScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {//如果触摸的是透明BlogTransView,那么该事件继续往下分发给BlogScrollView的子View:BlogTransViewif (mTouchEventListener != null && mTouchEventListener.isTouchTransView(ev)) {//返回false表示继续往下层View树进行分发return false;}//否则就调用ScrollView的拦截事件,进行滚动处理return super.onInterceptTouchEvent(ev);}//设置透明View的触摸事件监听器public void setTransViewTouchListener(BlogTransView.OnTouchEventListener listener) {mTouchEventListener = listener;}
}
  • 自定义透明View:BlogTransView
public class BlogTransView extends RelativeLayout {public static final String TAG = BlogTransView.class.getSimpleName();private CheckBox mBtnMapZoom;private CompoundButton.OnCheckedChangeListener mBtnZoomChangeListener;private DispatchEventListener mDispatchEventListener;//是否被触摸的监听器public interface OnTouchEventListener {boolean isTouchTransView(MotionEvent event);}//透明View的事件分发监听器public interface DispatchEventListener {boolean dispathTouchEvent(MotionEvent event);}public BlogTransView(Context context, AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.view_blog_trans, this, true);mBtnMapZoom = findViewById(R.id.btn_map_zoom);mBtnMapZoom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton compoundButton, boolean b) {if (mBtnZoomChangeListener != null) {mBtnZoomChangeListener.onCheckedChanged(compoundButton, b);}}});}//如果触摸了ScrollView上半部分的透明的部分,则事件会分发至此@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {if (mDispatchEventListener != null) {Rect rect = new Rect();mBtnMapZoom.getHitRect(rect);Log.v(TAG, "x:" + event.getX() + ";y:" + event.getY());//如果用户点击了放大,缩小按钮则该事件会继续往下分发给他的子View:CheckBoxif (rect.contains((int) event.getX(), (int) event.getY())) {Log.v(TAG, "mBtnMapZoom click yes");return super.dispatchTouchEvent(event);//继续往下分发给CheckBox} else {Log.v(TAG, "mBtnMapZoom click no");return mDispatchEventListener.dispathTouchEvent(event);}} else {return super.dispatchTouchEvent(event);}}public void setBtnZoomChangeListener(CompoundButton.OnCheckedChangeListener listener) {mBtnZoomChangeListener = listener;}public void setBtnMapZoomChecked(boolean b) {if (mBtnMapZoom != null) {mBtnMapZoom.setChecked(b);}}public boolean isTouchTransView(MotionEvent event){Rect rect = new Rect();getLocalVisibleRect(rect);//根据坐标来判断,是否是点击了透明View,如果是则返回true,否则返回falseif (rect.contains((int) event.getX(), (int) event.getY())) {return true;} else {return false;}}public void setDispatchEventListener(DispatchEventListener dispatchEventListener) {mDispatchEventListener = dispatchEventListener;}
}

缩放按钮事件响应

现在我们来解决第三个问题: 点击黑色缩放按钮地图放大缩小的问题,有人说很简单,把按钮加载地图上,因为NestedScrollView的事件传递给了透明View,而它再次把事件传递给了地图View,地图再把事件传递给缩放按钮,我确实也是这么做的,但是发现了一个问题,当点击按钮放大的时候,按钮能接受事件,当地图整屏显示之后,我们在点击按钮缩小地图的时候,很奇怪,通过代码测试发现无法判断此时的点击事件是在按钮之上,换了几个方法都行不通,不知道谁有什么好的解决办法,行不通了?怎么办,那就只能把缩放按钮放在透明View里了,但是又有问题了,当你手指在文字区域往上滑动的时候,缩放按钮回跟着往上移动,这是和我们的需求不符的。

  • 效果图如下:

    此时我再次脑洞大开我把透明View里的缩放按钮做成透明的,让他来响应触摸事件,然后在地图View的相同位置在放一个同样大小的缩放按钮,让它只做一个显示的作用,此时的逻辑就是这样的:当用户点击缩放按钮的时候NestedScorllView会把事件传递给透明View,如果此时触摸点在地图上就把该事件传递给地图,如果触摸点是在缩放按钮上就把事件继续往下分发给自己子View缩放按钮,然后缩放按钮添加的监听器响应事件,此时再来改变地图View中缩放按钮的状态,运行测试,大功告成,实在是,快哉!快哉!

  • BlogTransView的事件分发

//如果触摸了ScrollView上半部分的透明的部分,则事件会分发至此@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {if (mDispatchEventListener != null) {Rect rect = new Rect();mBtnMapZoom.getHitRect(rect);Log.v(TAG, "x:" + event.getX() + ";y:" + event.getY());//如果用户点击了放大,缩小按钮则该事件会继续往下分发给他的子View:CheckBoxif (rect.contains((int) event.getX(), (int) event.getY())) {Log.v(TAG, "mBtnMapZoom click yes");return super.dispatchTouchEvent(event);//继续往下分发给CheckBox} else {Log.v(TAG, "mBtnMapZoom click no");return mDispatchEventListener.dispathTouchEvent(event);}} else {return super.dispatchTouchEvent(event);}}
  • BlogMapView的事件分发
public class BlogMapView extends RelativeLayout {public static final String TAG = BlogMapView.class.getSimpleName();private AMap mMap;private TextureMapView mTextureMapView;private CheckBox mBtnMapZoom;private CompoundButton.OnCheckedChangeListener mChangeListener;public BlogMapView(Context context, AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.view_blog_map, this, true);mTextureMapView = findViewById(R.id.map);mMap = mTextureMapView.getMap();mMap.getUiSettings().setZoomControlsEnabled(false);mBtnMapZoom = findViewById(R.id.btn_map_zoom);mBtnMapZoom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton compoundButton, boolean b) {if (mChangeListener != null) {mChangeListener.onCheckedChanged(compoundButton, b);}}});}public boolean dispatchMapTouchEvent(MotionEvent event) {return mTextureMapView.dispatchTouchEvent(event);}public void setMapLayoutParams(int height) {setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height));mTextureMapView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, height));}public void setBtnMapZoomChecked(boolean b) {mBtnMapZoom.setChecked(b);}public void onCreate(Bundle bundle) {mTextureMapView.onCreate(bundle);}public AMap getMap() {return mMap;}public void setChangeListener(CompoundButton.OnCheckedChangeListener changeListener) {mChangeListener = changeListener;}
}

总结

截止目前整个案例我就讲完了,怎么样?是不是还是有些难度的,我觉得这个案例实在是太经典了,因为里面的一些事件传递问题和透明占位View的技巧使用,它涵盖了整个View事件分发的方方面面,如果对事件分发机制没有一定的理解和认识,还真搞不定它,只要把它搞懂了,我相信就能解决事件分发中的大部分问题了,最后我把整个测试项目放到github上了,希望对初学者有所帮助,测试过程中有什么问题可以在文章下方留言。

github源码下载地址:https://github.com/geduo83/android-touch-event

Android事件分发机制在实战开发中的应用之二相关推荐

  1. Android事件分发机制在实战开发中的应用之一

    学习的最终目标就是要学以致用,本文所分享的案例都是自己在公司实战开发过程中的真实案例,现在把它分享出来,希望对初学者有所帮助 版权声明:本文来自门心叼龙的博客,属于原创内容,转载请注明出处:https ...

  2. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  3. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  4. Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    <div id="container">         <div id="header">     <div class=&qu ...

  5. Android事件分发机制:基础篇:最全面、最易懂

    如何提升安卓水平?安卓开发者必须了解的事件分发机制. 最全面.最易懂的形式来讲解Android事件分发机制. 0. 前言 鉴于安卓分发机制较为复杂,故分为多个层次进行讲解,分别为基础篇.实践篇与高级篇 ...

  6. 一篇文章彻底搞懂Android事件分发机制

    本文讲的是一篇文章彻底搞懂Android事件分发机制,在android开发中会经常遇到滑动冲突(比如ScrollView或是SliddingMenu与ListView的嵌套)的问题,需要我们深入的了解 ...

  7. Android 事件分发机制分析及源码详解

    Android 事件分发机制分析及源码详解 文章目录 Android 事件分发机制分析及源码详解 事件的定义 事件分发序列模型 分发序列 分发模型 事件分发对象及相关方法 源码分析 事件分发总结 一般 ...

  8. 《Android深入透析》之Android事件分发机制

    <Android深入透析>之Android事件分发机制 <Android深入透析>之Android事件分发机制 一.      Android分发机制概述: Android如此 ...

  9. View的事件体系之三 android事件分发机制详解(下)

    接着上一篇来分析事件分发机制,在看了各位大牛的关于事件分发机制的分析后茅塞顿开,之前看过好几遍郭霖,弘扬以及玉刚大神关于事件体系的讲解,一直看不懂,比较模糊,最近复习时,看到一篇博文,写的相当精彩,看 ...

最新文章

  1. 最全干货:从写简历,到面试、谈薪酬的那些技巧和防坑指南
  2. 世界上将出现一种新职业
  3. 动态显示时采色改为单帧采色
  4. 操作主机 Infrastructure Master[为企业维护windows server 2008系列八]
  5. 对归并排序进行c语言编程实现,归并排序及C语言实现
  6. Android 属性动画ObjectAnimator使用demo,组合动画
  7. Linux 基础知识系列第一篇
  8. mysql checkpoint时机_MySQL Checkpoint机制
  9. 2019广州车展:国产版特斯拉Model 3正式亮相
  10. 深入解析MVVM架构
  11. 鸡兔同笼问题新解与Python实现
  12. oracle和mysql使用区别大吗_Oracle和MySQL在使用上的区别
  13. arg是什么函数_CTF必备技能丨Linux Pwn入门教程——针对函数重定位流程的相关测试(上)...
  14. 系统清理软件测试,系统垃圾清理有用吗?六大清理工具评测
  15. 网络爬虫——淘宝网页面分析思路
  16. python docx 表格复制粘贴_python Word 表格转 Excel
  17. java程序员待遇怎么样_现在的java程序员薪资待遇怎么样?
  18. ORACLE解析SOAPXML报文,xmltype extractvalue extract LPX-00210 中文乱码
  19. php 表情,php emoji表情处理
  20. 新版Uniapp开发多端影视APP源码+对接苹果CMS

热门文章

  1. C语言 数组元素的遍历
  2. 【VeighNa】开始量化交易——第四章:结合AHR999指标和情绪指标分析
  3. vue-cli3.0+webpack4中关于svg-sprite-loader踩坑,让svg组件徜徉我的web项目
  4. 编程5分钟,命名2小时:聊聊命名规范!
  5. ROS2机器人实验报告提示03➡迷雾⬅
  6. 对于阿里云手机 OS 大家都怎么看?
  7. UA OPTI570 量子力学16 含时的哈密顿量与时间演化算符
  8. CC2640R2F之NOTIFY发送子程序
  9. 初次使用 python poetry 包管理模块踩坑
  10. HTML5实现立方体及透视效果