排查状态栏沉浸式引起的布局间距过大的bug

记录一次在工作中遇到的排查状态栏沉浸式引起的布局间距过大的bug的过程。

我要在一个全屏的页面上(FrameLayout)悬浮一个置顶(Gravity.Top)的菜单栏(MenuBar)。

菜单栏初始状态是隐藏的,用一个translateY(-menuHeight)就可以做到这种效果。

当要显示菜单栏时,translateY(0)即可。当然隐藏和显示是通过属性动画来完成的。这里直奔主题,忽略一些实现细节。

我一开始做的时候,状态栏是沉浸式的状态(状态栏关闭),然后实现了我想要的效果。但是当我把状态栏沉浸式状态去掉(显示状态栏)之后,由于状态栏有一定的高度,我的手机上是60px,那么悬浮在上方的菜单栏(MenuBar)就应该处于状态栏的下方,也就是向下移动60px。

由于我的全屏的页面FrameLayout控件就是添加到了父页面(Fragment)的DecorView.contentView上面,也就是FrameLayout.Height=全屏高度-状态栏高度,所以自动会向下移动。这不需要我做额外的处理。

但是就在我认为大功告成的时候,诡异的问题出现了。

我的菜单栏不仅自动移动到了状态栏的下方,而且还继续向下移动了294px。就是这诡异294px让我一时无计可施。

我怀疑过是不是使用的父控件FrameLayout不支持动画,

怀疑过是不是对translateY的内部实现没理解清楚,我又看了一遍源码,发现无非就是个数学计算关系,没什么理解上的偏差。

经过大胆猜测之后,仍然没有任何头绪。

于是乎,我开始使用专业工具来一步步开始分析,把问题范围一点点缩小。

既然是UI展示上的问题,那就使用Android Studio自带的Layout Inspactor功能把视图树生成了一遍,从这里我拿到如下图所示的关键信息。可以看出问题出在mTop,y值上,而不是translationY,也不是marginTop(它们都是0)。所以我把排查注意力放到了mTop上。

那下面要做的就是找到是谁在修改mTop。然后我在菜单栏的最外层控件的代码中复写了如下两个方法。菜单栏的最外层控件是一个自定义的RelateLayout,这里我取名为DebugViewGroup。

@Override
public void setTranslationY(float translationY) {
    super.setTranslationY(translationY);
    LOG.D("debug_layout", "setTranslationY=" + translationY + ", y=" + getY() + ", top" + getTop());
}


@Override
public void setY(float y) {
    super.setY(y);
    LOG.D("debug_layout", "setY=" + y);
}

这两个方法打印的日志如下

02-22 21:55:30.244 14568 14568 D debug_layout: setTranslationY=-234.0, y=-234.0, top0
02-22 21:55:30.248 14568 14568 D debug_layout: setTranslationY=-234.0, y=-234.0, top0
02-22 21:55:30.249 14568 14568 D debug_layout: onMeasure
02-22 21:55:30.457 14568 14568 D debug_layout: onLayout top=0
02-22 21:55:30.483 14568 14568 D debug_layout: setTranslationY=-232.1509, y=61.849106, top294
02-22 21:55:30.495 14568 14568 D debug_layout: setTranslationY=-227.88892, y=66.111084, top294
02-22 21:55:30.512 14568 14568 D debug_layout: setTranslationY=-219.5279, y=74.47211, top294
02-22 21:55:30.530 14568 14568 D debug_layout: setTranslationY=-208.69212, y=85.30788, top294
02-22 21:55:30.545 14568 14568 D debug_layout: setTranslationY=-194.37349, y=99.62651, top294
02-22 21:55:30.562 14568 14568 D debug_layout: setTranslationY=-178.65385, y=115.346146, top294
02-22 21:55:30.580 14568 14568 D debug_layout: setTranslationY=-160.07057, y=133.92943, top294
02-22 21:55:30.595 14568 14568 D debug_layout: setTranslationY=-141.32564, y=152.67436, top294
02-22 21:55:30.612 14568 14568 D debug_layout: setTranslationY=-120.67505, y=173.32495, top294
02-22 21:55:30.629 14568 14568 D debug_layout: setTranslationY=-101.12129, y=192.87871, top294
02-22 21:55:30.646 14568 14568 D debug_layout: setTranslationY=-80.845, y=213.155, top294
02-22 21:55:30.662 14568 14568 D debug_layout: setTranslationY=-62.794376, y=231.20563, top294
02-22 21:55:30.679 14568 14568 D debug_layout: setTranslationY=-45.28986, y=248.71014, top294
02-22 21:55:30.695 14568 14568 D debug_layout: setTranslationY=-30.87664, y=263.12335, top294
02-22 21:55:30.711 14568 14568 D debug_layout: setTranslationY=-18.213646, y=275.78635, top294
02-22 21:55:30.729 14568 14568 D debug_layout: setTranslationY=-9.142019, y=284.85797, top294
02-22 21:55:30.744 14568 14568 D debug_layout: setTranslationY=-2.817727, y=291.18228, top294
02-22 21:55:30.760 14568 14568 D debug_layout: setTranslationY=-0.16034031, y=293.83966, top294
02-22 21:55:30.776 14568 14568 D debug_layout: setTranslationY=0.0, y=294.0, top294

从日志上看出,动画过程中有未知的代码给top设置了值。debug了setY和setTranslationY这两个方法却没找到这个诡异的调用者,可以推断出来,它不是通过这两个方法给mTop设置的值。那为了找到给mTop设置值的调用者,只能看源码中有哪些方法在给mTop设置值。

View源码中会改动mTop值的方法只有这三个,如下图所示。所以我就在这三个方法的调用链中查找。由于我的手机不能调试源码,所以这条线索并没有找到问题的答案。 如果有台可以调试源码的手机,只需要在这三个方法上打上断点,问题就解决了。

那我只能选择另一个办法就是给mTop成员变量打一个断点。虽然调试成员变量时android studio运行性能比较差,但是通过设置条件断点就可以提升Android Studio运行性能。断点如下:

断点的条件 表达式如下:

this instanceof DebugViewGroup

通过这个断点,我发现了如下的调用链, 

这个调用链对应于项目中的源码,截取部分代码如下: 

topView.getGlobalVisibleRect(rect);
int curTop = rect.top;
int toTop = Math.max(statusView.getBottom(), curTop);
topView.offsetTopAndBottom(toTop - curTop);

其实就是因为在Activity基类中监听了ViewTreeObserver.OnGlobalLayoutListener。然后拿到了我的菜单栏引用(topView)并且修改了它的top值。到这里真是感慨项目中坑太多,不得不在笔记里给他记一颗黑豆了。

至于如何解决就很简单了,既然菜单栏的移动已经通过translationY的方式设置了一遍了,这里的设置直接删除就可以了。

发布了348 篇原创文章 · 获赞 8 · 访问量 74万+

猜你喜欢

转载自blog.csdn.net/logan676/article/details/104452010