长按BottomNavigationView的子Item为什么会弹出的Toast?

快速了解

  • 解决的问题
    长按Item会弹出Toast

  • BottomNavigationView依赖版本:implementation 'com.google.android.material:material:1.2.0'

  • BottomNavigationView的层级结构:
    BottomNavigationView层级结构
    看图我们可以知道,BottomNavigationView有一个直接子View(BottomNavigationMenuView)里面装的是每一个Menu的Item(BottomNavigationItemView)这就是我们点击或长按item时的实际的View。

  • 获取具体BottomNavigationItemView的方式

bottomNavigationView.getChildView(0).findViewById(R.id.menu3)

猜想与验证

  • 猜想1:既然长按Item会出现Toast,那么将具体BottomNavigationItemView设置长按事件LongClickListener为null就不会触发长按事件了,那是不是就不会弹出Toast了呢?
    答:无效,长按仍然会显示Toast,我不是将LongClick置空了吗!!为什么?接下来带你一步步看。
  • 猜想2:🔍一下别人的解决方式,设置BottomNavigationView的长按监听事件并且返回true;
    答:有效!,如果你只想解决问题,那么使用下方代码即可解决。
//注意BottomNavigationItemView,是具体点击的那个按钮控件~~删除线格式~~ 
BottomNavigationItemView.setOnLongClickListener{true}//lambda表达式、返回true代表消费这个事件
  • 验证:为什么将BottomNavigationItemView的长按事件设置null不起作用,但是给它设置一个长按事件并返回true即可解决呢?
  1. 从BottomNavigationItemView的源码入手;

没有长按事件相关的逻辑
我们可以看到没有任何长按相关的逻辑,也就是说给BottomNavigation设置menu的时候,它并不会自动的帮我们设置长按事件。没有长按事件,我们就从弹出的文字入手,弹出的Toast显示的menu的title,那么我们看下他的title在哪里被使用了!

title的使用位置

通过上面的源码方法我们可以发现title被设置在smallLabellargeLabel两个TextView上,还有最后一行的TooltipCompat.setTooltipText(this, tooltipText);使用了title,跟进去看看

TooltipCompat#setTooltipText方法
这个是一个兼容静态方法,我们继续更进去看API大于等于26的实现(小于26的自行分析啦);

View#setTooltipText
我们可以看它的英文注释描述,Sets the tooltip text which will be displayed in a small popup next to the view大意是通过它会利用一个的弹框展示文本内容,这里的文本内容不就是我们传进来的title吗!!它将传进来的title放到mTooltipInfo.mTooltipText = tooltipText;里,我们看一下mTooltipText在哪里被使用了,有好几处,但是关键的是一个showTooltip的方法

View#showTooltip

该方法的主要作用就是构建一个TooltipPopup弹框(Toast其实也是一个弹框)并显示。已经找到了显示Toast的方法了,那么我们看看它何时被调用。

View#showTooltip在哪里被调用

上图可以看到showLongClickTooltip的方法,看方法名好像是和长按事件有关,继续看一下它被谁调用

showLongClickTooltip被调用的一处
它被两处调用,但是这一处View.performLongClickInternal最关键,这不就是触发View的长按事件的方法吗!我们仔细看一下这个方法的实现
handled临时变量来保存是否被处理
第一个if,它判断是否有设置OnLongClickListener长按监听,如果有则调用OnLongClick的方法,并且将返回值赋值给handled,如果handled为true则不会走以下的两个if;第二个if是如果handled为false,它会去看是否要显示上下文菜单ContextMenu(可以具体🔍一下是什么东西),并将返回值赋值给handled,第三个if是我们关注的那个显示Toast的方法,如果上面两个都未被处理,则会调用它;

这个方法很好的解释了,为什么设置OnLongClickListener为null的时候,仍然会显示Toast,因为它handled仍为false,第二个if如果没有用上下文菜单默认也是false,进而会继续调用showLongClickTooltip的方法。这也能说明为什么设置OnLongClickListener返回true就不会显示Toast了。到这里你就可以明白长按BottomNavigationView的菜单为什么会显示Toast了,接下来我们继续看,我设置长按事件为null了,为什么仍然会走这个方法呢?

View#performClick

它的直接调用者是performLongClick方法,那么我们看下谁又调用它

View$CheckForLongPress

它被调用的地方有好几处,最终找到这个Runnable子类CheckForLongPress中调用的是关键,我们继续看它在哪里被实例化

View#checkForLongClick

它在checkForLongClick方法被实例化,最终调用postDelayed(mPendingCheckForLongPress, delay);将上面的Runnable方法放到消息队列中等待一定的延时后调用runnable进而调用显示Toast的方法;继续看谁又调用它,我们发现最终它会被View#onTouchEvent调用,终于回到了事件分发机制(理解它的流程很关键!!)的流程

View#onTouchEvent
它在Down事件中去检查LongClick,所以不管你设不设置长按监听,依然会走上方的流程。

总结

  • 设置BottomNavigationView的menu不会自动给Item设置长按监听,但是会给Item的View设置tooltipText
  • 长按事件的触发仍然是走事件分发机制的那套流程,它在Down事件的时候去调用View.checkForLongClick,即便你不设置长按监听也会走接下来的流程;它是在特定条件不满足的情况下会执行View.showTooltip方法将ItemView的tooltipText通过一个弹窗进行显示出来;
  • 特定条件是,设置了onLongClickListener并且OnLongClick方法中返回true,设置了上下文菜单,并且它的方法也返回true,否则长按就会显示title的Toast;
  • 经过一下午的研究,结合网上给的答案,终于解决为什么长按BottomNavigationView的item会弹出Toast了!!好开心即便这仅是一个小小的细节,写这个文章是记录自己一次通过分析源码解决问题O(∩_∩)O哈哈~
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值