Android事件分发机制四:学了事件分发有什么用?,来来来

这些是我们使用得最频繁的监听器,他们之间的关系是:

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©

这样就实现了按钮的双击监听了。

其他类型的监听器如:三击、双击长按等等,都可以基于这种思路来实现监听器接口。

自定义view


在自定义view中,我们可以更加灵活地运用事件分发来解决实际的需求。举几个例子:

滑动嵌套问题:外层是viewPager,里层是recyclerView,要实现左右滑动切换viewPager,上下滑动recyclerView。这也就是著名的滑动冲突问题。类似的还有外层viewPager,里层也是可以左右滑动的recyclerView。 实时触摸反馈问题:如设计一个按钮,要让他按下的时候缩小降低高度,放开的时候恢复到原来的大小和高度,而且如果在一个可滑动的容器中,按下之后滑动不会触发点击事件而是把事件交给外层可滑动容器。

我们可以发现,基本上都是基于实际的开发需求来灵活运用事件分发。具体到代码实现,都是围绕着三个关键方法展开: dispatchTouchEvent、 onIntercepterTouchEvent 、 onTouchEvent 。这三个方法在view和viewGroup中已经有了默认的实现,而我们需要基于默认实现来完成我们的需求。下面看看几种常见的场景如何实现。

实现方块按下缩小

我们先来看看具体的实现效果:

方块按下后,会缩小高度变低透明度增加,释放又会恢复。

这个需求可以通过结合属性动画来实现。按钮块本身有高度、有圆角,我们可以考虑继承cardView来实现,重写cardView的dispatchTouchEvent方法,在按下的时候,也就是接收到down事件的时候缩小,在接收到up和cancel事件的时候恢复。注意,这里可能会忽视cancel事件,导致按钮块的状态无法恢复,一定要加以考虑cancel事件 。然后来看下代码实现:

public class NewCardView extends CardView {

//点击事件到来的时候进行判断处理

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

// 获取事件类型

int actionMarked = ev.getActionMasked();

// 根据时间类型判断调用哪个方法来展示动画

switch (actionMarked){

case MotionEvent.ACTION_DOWN :{

clickEvent();

break;

}

case MotionEvent.ACTION_CANCEL:

case MotionEvent.ACTION_UP:

upEvent();

break;

default: break;

}

// 最后回调默认的事件分发方法即可

return super.dispatchTouchEvent(ev);

}

//手指按下的时候触发的事件;大小高度变小,透明度减少

private void clickEvent(){

setCardElevation(4);

AnimatorSet set = new AnimatorSet();

set.playTogether(

ObjectAnimator.ofFloat(this,“scaleX”,1,0.97f),

ObjectAnimator.ofFloat(this,“scaleY”,1,0.97f),

ObjectAnimator.ofFloat(this,“alpha”,1,0.9f)

);

set.setDuration(100).start();

}

//手指抬起的时候触发的事件;大小高度恢复,透明度恢复

private void upEvent(){

setCardElevation(getCardElevation());

AnimatorSet set = new AnimatorSet();

set.playTogether(

ObjectAnimator.ofFloat(this,“scaleX”,0.97f,1),

ObjectAnimator.ofFloat(this,“scaleY”,0.97f,1),

ObjectAnimator.ofFloat(this,“alpha”,0.9f,1)

);

set.setDuration(100).start();

}

}

动画方面的内容就不分析了,不属于本文的范畴。可以看到我们只是给cardView设置了动画效果,监听事件我们可以设置给cardView内部的ImageView或者直接设置给CardView。需要注意的是,如果设置给cardView需要重写cardView的 intercepTouchEvent 方法永远返回true,防止事件被子view消费而无法触发监听事件。

解决滑动冲突

滑动冲突是事件分发运用最频繁的场景,也是一个重难点(敲黑板,考试要考的)。滑动冲突的基本场景有以下三种:

  • 情况一:内外view的滑动方向不同,例如viewPager嵌套ListView

  • 情况二:内外view滑动方向相同,例如viewPager嵌套水平滑动的recyclerView

  • 情况三:情况一和情况二的组合

解决这类问题一般有两个步骤:确定最终实现效果、代码实现。

滑动冲突的解决需要结合具体的实现需求,而不是一套解决方案可以解决一切的滑动冲突问题,这不现实。因此在解决这类问题时,需要先确定好最终的实现效果,然后再根据这个效果去思考代码实现。这里主要讨论情况一和情况二,情况三类同。

情况一

情况一是内外滑动方向不一致。这种情况的通用解决方案就是:根据手指滑动直线与水平线的角度来判断是左右滑动还是上下滑动:

如果这个角度小于45度,可以认为是在左右滑动,如果大于45度,则认为是上下滑动。那么现在确定好解决方案,接下来就思考如何代码实现。

滑动角度可以通过两个连续的MotionEvent对象的坐标计算出来,之后我们再根据角度的情况选择把事件交给外部容器还是内部view。这里根据事件处理的位置可分为内部拦截法和外部拦截法 。

  • 外部拦截法:在viewGroup中判断滑动的角度,如果符合自身滑动方向消费则拦截事件

  • 内部拦截法:在内部view中判断滑动的角度,如果是符合自身滑动方向则继续消费事件,否则请求外部viewGroup拦截事件处理

从实现的复杂度来看,外部拦截法会更加优秀,不需要里外view去配合,只需要viewGroup自身做好事件拦截处理即可。两者的区别就在于主动权在谁的手上。如果view需要做更多的判断可以采用内部拦截法,而一般情况下采用外部拦截法会更加简单。

接下来思考一下这两种方法的代码实现。


外部拦截法中,重点在于是否拦截事件,那么我们的重心就放在了 onInterceptTouchEvent 方法中。在这个方法中计算滑动角度并判断是否要进行拦截。这里以ScrollView为例子(外部是垂直滑动,内部是水平滑动),代码如下:

public class MyScrollView extends ScrollView {

// 记录上一次事件的坐标

float lastX = 0;

float lastY = 0;

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

int actionMasked = ev.getActionMasked();

// 不能拦截down事件,否则子view永远无法获取到事件

// 不能拦截up事件,否则子view的点击事件无法被触发

if (actionMasked == MotionEvent.ACTION_DOWN || actionMasked == MotionEvent.ACTION_UP){

lastX = ev.getX();

lastY = ev.getY();

return false;

}

// 获取斜率并判断

float x = ev.getX();

float y = ev.getY();

return Math.abs(lastX - x) < Math.abs(lastY - y);

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF,大家有兴趣的可以自行领取或者私信我:

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】点击:Android架构视频+BAT面试专题PDF+学习笔记即可获取!查看免费领取方式!

备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。**

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

以上就是总结的关于在面试的一些总结,希望对大家能有些帮助,除了这些面试中需要注意的问题,当然最重要的就是刷题了,这里放上我之前整理的一份超全的面试专题PDF,大家有兴趣的可以自行领取或者私信我:

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】点击:Android架构视频+BAT面试专题PDF+学习笔记即可获取!查看免费领取方式!

[外链图片转存中…(img-t9wkeK27-1711165231891)]

这里只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢在关注一下~

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值