文章目录
- 前言
- 一、为什么样式会变?
- 二、调试发现原因并解决
- 1.找到原因
- 2.解决
- 总结
前言
最近在负责一些UI相关的工作,测试给到一个UI的bug,说是搜索框在点击的时候,旁边的’‘X’'变成按压的效果了,我转手就把bug转给负责公控的同事了,因为这个搜索框是公控同事提供的,但是公控大佬不一会就找到我说,我这里都是好的鸭,是不是你哪里没用对,我直接就晕乎了,我左看右看也没干啥鸭,于是开始对线,巴拉巴拉…但是问题还是没有解决,于是有了这篇文章.
一、为什么样式会变?
首先公控大佬给我讲了他是怎么做X的按压效果的,就是说监听了搜索框的onTouchEvent事件,然后当onTouchEvent传入的rect包含了X的时候,将其设置为点按效果,其他时候是不会对这个X控件的样式进行改变的,没办法,我只好将公控同事的源码要来,加上日志调试.
二、调试发现原因并解决
1.找到原因
加了日志发现代码并没有走到公控同事的代码逻辑当中,但是进入onTouchEvent事件时,X控件的样式已经变成点按状态了,而公控同事的demo项目,X控件的样式确实并没有改变,鉴定为G.最后监听setPressed的时候发现在点击控件时,会触发 setPressed = true 事件,随后分析VIEW 源码,定位到如下代码位置:
case MotionEvent.ACTION_DOWN:.....这里忽略部分代码// Walk up the hierarchy to determine if we're inside a scrolling container.boolean isInScrollingContainer = isInScrollingContainer();// For views inside a scrolling container, delay the pressed feedback for// a short period in case this is a scroll.if (isInScrollingContainer) {mPrivateFlags |= PFLAG_PREPRESSED;if (mPendingCheckForTap == null) {mPendingCheckForTap = new CheckForTap();}mPendingCheckForTap.x = event.getX();mPendingCheckForTap.y = event.getY();postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());} else {// Not inside a scrolling container, so show the feedback right away setPressed(true, x, y);checkForLongClick(ViewConfiguration.getLongPressTimeout(),x,y,TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);}break;
在View.java的源码当中的onTouchEvent方法的MotionEvent.ACTION_DOWN事件,有一个 isInScrollingContainer的醒目判断,并加上了注释,我们继续看下isInScrollingContainer方法
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)public boolean isInScrollingContainer() {ViewParent p = getParent();while (p != null && p instanceof ViewGroup) {if (((ViewGroup) p).shouldDelayChildPressedState()) {return true;}p = p.getParent();}return false;}
可以看到会判断一个shouldDelayChildPressedState方法,然后返回true,到这里基本可以确定这就是我和公控同事触发的不一致的地方,这是一个ViewGroup的方法,点进去过后注释是这样写的:
Return true if the pressed state should be delayed for children or
descendants of this ViewGroup. Generally, this should be done for
containers that can scroll, such as a List. This prevents the pressed
state from appearing when the user is actually trying to scroll the
content. The default implementation returns true for compatibility
reasons. Subclasses that do not scroll should generally override this
method and return false.
我们机翻一下:如果应延迟此ViewGroup的子代或子代的按下状态,则返回true。通常,这应该针对可以滚动的容器(如List)进行。这防止了当用户实际试图滚动内容时出现按下状态。由于兼容性原因,默认实现返回true。不滚动的子类通常应该覆盖此方法并返回false。
2.解决
到了上面这里,我们基本可以明白,这是因为我们搜索框公控的父布局使用的可滑动的控件,导致进入isInScrollingContainer返回true,并触发了setPressed方法,然后改变了控件的样式 为点按状态,针对这个问题,我们有这几种方式可以解决.
- 重写公控的这个控件的setPressed方法,每次都返回false
- 重写父布局的shouldDelayChildPressedState方法,使其返回false
- 更改公控代码对 这种特殊情况做兼容
前面两种方法改的快,但是不能确保不出其它问题,因此这个问题最后还是由公控大佬对这种情况做兼容,over
总结
通过调试和分析源码,我们找到了问题的原因和解决方法。问题是因为在可滑动的父布局中,触发了setPressed方法,导致控件样式变为点按状态。解决方法有重写setPressed方法、重写shouldDelayChildPressedState方法或者对公控代码做兼容处理。选择合适的解决方法,能够解决这个UI bug。