-
概述
应用BottomNavigationView的时候偶然发现长按菜单项的时候会弹出一个toast,toast的内容和菜单项的title是一致的,通常我们不会喜欢有这么个画蛇添足的效果,想着能不能把这个效果关掉,但是看过源码后没发现可配置这个效果的属性,也没发现设置长按事件的代码,最后发现一个View内嵌的长按设置,比较新颖,记录一下。
-
最初思路的源码分析
最开始,找到NavigationBarMenuView中添加item的地方:
public void buildMenuView() { removeAllViews(); ... buttons = new NavigationBarItemView[menu.size()]; ... for (int i = 0; i < menu.size(); i++) { ... NavigationBarItemView child = getNewItem(); buttons[i] = child; ... int itemId = item.getItemId(); child.setOnTouchListener(onTouchListeners.get(itemId)); child.setOnClickListener(onClickListener); ... addView(child); } ... }
可以看到,这里并没有发现child调用setOnLongClickListener方法,buttons也搜了,没有额外的设置长按事件,BottomNavigationView、NavigationBarView、NavigationBarItemView都没搜到设置长按。
事情好像开始无解了。
-
新鲜的东西
常规思路无法解决的时候,我试着去网上搜一搜,结果有很多说设置:
//遍历子View,重写长按点击事件 for (position in 0 until ids.size){ bottomNavigationMenuView.getChildAt(position).findViewById<View>(ids[position]).setOnLongClickListener { true } }
这还真的奏效了,不过你不能设置setOnLongClickListener为null。
那这是为什么呢?
然后我搜到了一位同学写的针对这个问题Why的源码分析。
在NavigationBarItemView中的setTitle方法中:
@Override public void setTitle(@Nullable CharSequence title) { smallLabel.setText(title); largeLabel.setText(title); ... if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP || VERSION.SDK_INT > VERSION_CODES.M) { TooltipCompat.setTooltipText(this, tooltipText); } }
重点在于TooltipCompat.setTooltipText:
public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) { if (Build.VERSION.SDK_INT >= 26) { view.setTooltipText(tooltipText); } else { TooltipCompatHandler.setTooltipText(view, tooltipText); } }
小于26版本时TooltipCompatHandler.setTooltipText中会设置长按事件为null,而在26版本以上的才会设置长按事件,接着往下看:
public void setTooltipText(@Nullable CharSequence tooltipText) { if (TextUtils.isEmpty(tooltipText)) { setFlags(0, TOOLTIP); hideTooltip(); mTooltipInfo = null; } else { setFlags(TOOLTIP, TOOLTIP); if (mTooltipInfo == null) { mTooltipInfo = new TooltipInfo(); mTooltipInfo.mShowTooltipRunnable = this::showHoverTooltip; mTooltipInfo.mHideTooltipRunnable = this::hideTooltip; mTooltipInfo.mHoverSlop = ViewConfiguration.get(mContext).getScaledHoverSlop(); mTooltipInfo.clearAnchorPos(); } mTooltipInfo.mTooltipText = tooltipText; } }
这里会根据tooltipText是否为空来决定是否设置TOOLTIP这个flag,如果不为空则设置TOOLTIP,然后再往下就没逻辑了,我们还是没有找到设置长按事件的地方。
但是我们可以找到show的方法:
private boolean showTooltip(int x, int y, boolean fromLongClick) { if (mAttachInfo == null || mTooltipInfo == null) { return false; } if (fromLongClick && (mViewFlags & ENABLED_MASK) != ENABLED) { return false; } if (TextUtils.isEmpty(mTooltipInfo.mTooltipText)) { return false; } hideTooltip(); mTooltipInfo.mTooltipFromLongClick = fromLongClick; mTooltipInfo.mTooltipPopup = new TooltipPopup(getContext()); final boolean fromTouch = (mPrivateFlags3 & PFLAG3_FINGER_DOWN) == PFLAG3_FINGER_DOWN; mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, mTooltipInfo.mTooltipText); mAttachInfo.mTooltipHost = this; // The available accessibility actions have changed notifyViewAccessibilityStateChangedIfNeeded(CONTENT_CHANGE_TYPE_UNDEFINED); return true; }
可以看到,这里通过TooltipPopup(可以认为是一个PopupWindow,只是通过WindowManager.addView来添加的)来展示的。
我们找一下showTooltip方法调用的地方,发现有一个showLongClickTooltip方法,查看他的调用链,发现源头在onTouchEvent的ACTION_DOWN分支下:
public boolean onTouchEvent(MotionEvent event) { ... if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { switch (action) { case MotionEvent.ACTION_UP: mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; //这里控制在手指抬起后经过一定时间才隐藏toast if ((viewFlags & TOOLTIP) == TOOLTIP) { handleTooltipUp(); } ... break; case MotionEvent.ACTION_DOWN: ... // For views inside a scrolling container, delay the pressed feedback for // a short period in case this is a scroll. if (isInScrollingContainer) { ... } else { // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y); //在这里触发到showTooltip中去的 checkForLongClick( ViewConfiguration.getLongPressTimeout(), x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } break; case MotionEvent.ACTION_CANCEL: ... break; case MotionEvent.ACTION_MOVE: ... break; } return true; } return false; }
private void checkForLongClick(long delay, float x, float y, int classification) { if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.setAnchor(x, y); mPendingCheckForLongPress.rememberWindowAttachCount(); mPendingCheckForLongPress.rememberPressedState(); mPendingCheckForLongPress.setClassification(classification); postDelayed(mPendingCheckForLongPress, delay); } }
checkForLongClick方法中会调用postDelayed方法,delay是ViewConfiguration.getLongPressTimeout(),就是延迟长按的时间,运行的Runnable就是mPendingCheckForLongPress,它是CheckForLongPress类型的:
private final class CheckForLongPress implements Runnable { ... @Override public void run() { if ((mOriginalPressedState == isPressed()) && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { recordGestureClassification(mClassification); if (performLongClick(mX, mY)) { mHasPerformedLongPress = true; } } } ... }
这里会调用performLongClick,最终会调用到performLongClickInternal方法:
private boolean performLongClickInternal(float x, float y) { ... boolean handled = false; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnLongClickListener != null) { handled = li.mOnLongClickListener.onLongClick(View.this); } ... if ((mViewFlags & TOOLTIP) == TOOLTIP) { if (!handled) { handled = showLongClickTooltip((int) x, (int) y); } } ... return handled; }
因为我们之前在setTooltipText方法中设置了TOOLTIP,因此这里只要handled为false就能执行showLongClickTooltip方法,如果没有设置OnLongClickListener并且在其onLongClick方法中返回true的话,showLongClickTooltip肯定是会被执行的,这也就是为什么BottomNavigationView的菜单项默认会有长按弹出toast的效果,也是为什么给NavigationBarItemView设置OnLongClickListener为null后仍然无法禁止长按弹出toast的效果的原因。
看到这,你可能知道应该怎么做来禁用这个效果了。
//禁用item的长按事件 val menuView: NavigationBarMenuView = getChildAt(0) as NavigationBarMenuView for (index in 0 until menuView.childCount) { menuView.getChildAt(index).setOnLongClickListener { true } }
BottomNavigationView的Item长按为什么会弹出toast
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- View的事件体系 View的基础 view位置参数View的位置主要由它的四个顶点来决定,分别对应于View的四...