“ ..... ”
前言Android事件分发机制已经来到第四篇了,在前三篇中:
Android事件分发机制一:事件是如何到达activity的? : 从window机制出发分析了事件分发的整体流程,以及事件分发的真正起点
Android事件分发机制二:viewGroup与view对事件的处理 : 源码分析了viewGroup和view是如何分发事件的
Android事件分发机制三:事件分发工作流程 : 分析了触摸事件在控件树中的分发流程模型
那么关于事件分发的知识在上面三篇文章也就分析地差不多了,接下来就分析一下学了之后该如何使运用到实际开发中,简单阐述一下笔者的思考。
Android中的view一般由两个重要的部分组成:绘制和触摸反馈。如何精准地针对用户的操作给出正确的反馈,是我们学事件分发最重要的目标。
运用事件分发一般有两个场景:给view设置监听器和自定义view。接下来就针对这两方面展开分析,最后再给出笔者的一些思考与总结。
监听器触摸事件监听器可以说是我们接触Android事件体系的第一步。监听器通常有:
OnClickListener : 单击事件监听器
OnLongClickListener : 长按事件监听器
OnTouchListener : 触摸事件监听器
这些是我们使用得最频繁的监听器,他们之间的关系是:
if (mOnTouchListener!=null && mTouchListener.onTouch(event)){ return true; }else{ if (单击事件){ mOnClickListener.onClick(view); }else if(长按事件){ mOnLongClickListener.onLongClick(view); } }上面的伪代码可以很明显地发现:onTouchListener是直接把MotionEvent对象直接接管给自己处理且会最先调用,而其他的两个监听器是view判断点击类型之后再分别调用 。
除此之外,另一个很常见的监听器是双击监听器,但这种监听器并不是view默认支持的,需要我们自己去实现。双击监听器的实现思路可以参考view实现长按监听器的思路来实现:
当我们接收到点击事件时,可以发送一个单击延时任务。如果在延迟时间到还没收到另一个点击事件,那么这就是一个单击事件;如果在延迟时间内收到另一个点击事件,说明这是一个双击事件,并取消延时任务。
我们可以实现 view.OnClickListener 接口来完成以上逻辑,核心代码如下:
// 实现onClickListener接口 class MyClickListener() : View.OnClickListener{ private var isClicking = false private var singleClickListener : View.OnClickListener? = null private var doubleClickListener : View.OnClickListener? = null private var delayTime = 1000L private var clickCallBack : Runnable? = null private var handler : Handler = Handler(Looper.getMainLooper()) override fun onClick(v: View?) { // 创建一个单击延迟任务,延迟时间到了之后触发单击事件 clickCallBack = clickCallBack?: Runnable { singleClickListener?.onClick(v) isClicking = false } // 如果已经点击过一次,在延迟时间内再次接受到点击 // 意味着这是个双击事件 if (isClicking){ // 移除延迟任务,回调双击监听器 handler.removeCallbacks(clickCallBack!!) doubleClickListener?.onClick(v) isClicking = false }else{ // 第一次点击,发送延迟任务 isClicking = true handler.postDelayed(clickCallBack!!,delayTime) } } ... }代码中实现了创建了一个 View.OnclickListener 接口实现类,并在类型实现单击和双击的逻辑判断。我们可以如下使用这个类:
val c = MyClickListener() // 设置单击监听事件 c.setSingleClickListener(View.OnClickListener { Log.d(TAG, "button: 单击事件") }) // 设置双击监听事件 c.setDoubleClickListener(View.OnClickListener { Log.d(TAG, "button: 双击事件") }) // 把监听器设置给按钮 button.setOnClickListener(c)这样就实现了按钮的双击监听了。
其他类型的监听器如:三击、双击长按等等,都可以基于这种思路来实现监听器接口。
自定义view在自定义view中,我们可以更加灵活地运用事件分发来解决实际的需求。举几个例子:
滑动嵌套问题:外层是viewPager,里层是recyclerView,要实现左右滑动切换viewPager,上下滑动recyclerView。这也就是著名的滑动冲突问题。类似的还有外层viewPager,里层也是可以左右滑动的recyclerView。
实时触摸反馈问题:如设计一个按钮,要让他按下的时候缩小降低高度,放开的时候恢复到原来的大小和高度,而且如果在一个可滑动的容器中,按下之后滑动不会触发点击事件而是把事件交给外层可滑动容器。
我们可以发现,基本上都是基于实际的开发需求来灵活运用事件分发。具体到代码实现,都是围绕着三个关键方法展开: dispatchTouchEvent 、 onIntercepterTouchEvent 、 onTouchEvent 。这三个方法在view和viewGroup中已经有了默认的实现,而我们需要基于默认实现来完成我们的需求。下面看看几种常见的场景如何实现。
实现方块按下缩小我们先来看看具体的实现效果:
方块按下后,会缩小高度变低透明度增加,释放又会恢复。