引言
去年我开源了一个RecyclerView吸顶库,关于它的介绍请大家移步“我开源了一个RecyclerView吸顶库”一文。别看这个库大量使用了泛型,对象池、链表等技术,但是吸顶的功能真正倚仗的只有ViewCompat.offsetTopAndBottom(View view,int offset)这一个方法。它能够帮我们实现将View上下平移。它的姐妹方法offsetLeftAndRight(View view, int offset)可以实现将View左右平移,一般情况使用它们实现平移效果非常简单,适合各种阶段的Android开发者使用,堪称老少咸宜,效果那是童叟无欺。但是在我的吸顶库中,偏偏在某些特殊的情况下,调用了offsetTopAndBottom方法,View并没有发生预期的偏移效果,而是呆呆的停留在原地,我靠,翻车了!本来想从源码的角度正面寻找这个问题的答案,奈何offsetTopAndBottom虽然使用简单,但是源码一点都不简单。正面攻破不了,能否有办法从侧面解决呢?下面我将还原一下翻车现场,以及几个侧面解决的方案。
还原翻车现场
这是来自我吸顶库中的一段代码,这里有翻车现场,如果你看不懂,也没关系,后面我会用一个非常简单的例子来还原这个现场。这段代码的功能是处理向下滑动RecyclerView时,切换吸顶功能。我们注意到addStickyView(newStickyView)方法将newStickyView视图添加到mStickyHeaderLayout布局中,紧接着调用ViewCompat.offsetTopAndBottom(mStickyHeaderLayout, mStickyHeaderLayoutTop - mStickyHeaderLayout.getMeasuredHeight() - mStickyHeaderLayout.getTop()),想要实现的效果是将mStickyHeaderLayout隐藏掉。问题就在于mStickyHeaderLayout此刻并没有隐藏掉,而是岿然不动的停留在原来的位置。所以在手机上的效果是,向下滑动RecyclerView时,当发生吸顶切换时,会有一个让人非常不适的闪动效果,当时出现这个问题时,我的内心肯定是在骂人的了,极度不理解,为啥调用了offset方法,却没有发生偏移呢,如果不解决这个问题,那么之前在该库上花的心血几乎要功亏一篑了。没办法,只能擦干眼泪找原因,找解决方案了。首先一个非常值得怀疑的点是,是不是因为先调用了addView 再调用offset方法,导致失效了呢?写个DEMO来验证一下。
DEMO还原现场
上面吸顶库的代码比较复杂,那我来写个简单的DEMO验证一下上面的疑问。代码我已上传至github,https://github.com/lizijin/zijiexiaozhan
上述代码,模拟了在吸顶库中遇到的问题,在addView后立马调用offset,点击“ADD AND OFFSET”按钮效果如下,仅仅是把newButton添加到屏幕中,并没有向下平移100像素。
解决问题
已经还原了问题现场,下一步就是定位问题和解决问题了。熟悉View.post(Runnable action)技巧的同学可能会第一时间想到用View.post来解决这个问题。我的第一反应也是这么想的。于是信手拈来,加个post。
1.1 使用post方式解决
1.2 效果
能够完美的解决不偏移的问题,但是引入了新问题,真机上可以明显看到闪烁。gif图由于分辨率的问题,看不出闪烁
2.1 使用post方式+setAlpha解决
既然有闪烁,于是我思索了好久,如何能解决这种肉眼可见的闪烁bug呢?突然有一天,灵光一现,能否在偏移前后,设置它的透明度呢?post之前setAlpha(0),post时setAlpha(1),于是尝试一番,果不其然能够解决闪烁的问题。
2.2 效果
能够完美的解决不偏移和闪烁的问题。但是在吸顶库中又引入了新的问题,即快速向下滑动的过程中,吸顶后面的视图会不合时宜的出现在视觉中,造成另外一种情况的闪烁
3.1 使用post方式+OnPreDrawListener解决
使用post+alpha在快速滑动时会出现,吸顶短暂消失,漏出吸顶背后的View,这种闪烁的效果如果交互给用户,肯定会被喷的。于是又陷入了苦苦思索的状态,然后从View的绘制流程的各个节点尝试了一遍,了无收获,直到有一天尝试了下ViewTreeObserver.addOnPreDrawListener方法,意外的发现可以解决以上的问题,开心~ 解决方案
3.2 效果
完美解决了以上的所有问题