前文
长复杂页面在项目中采用listview实现的形式,利用mergeAdapter来对界面做控制,偏偏有个itemView 是viewpager的形式,于是该itemView里有个ViewPager作为子view来做横滑操作。做出后,看上去很美。但是,突然发现当该item划出屏幕,再划回来的时候,第一次切换viewPager总会秒切,而且不丝滑,但之后的切换操作并无什么异常,都很丝滑。强迫症受不了,必须搞。而且只在7.x的版本上有问题,在4.X,6.X上都很perfect。
分析
- 首先是冥想阶段
- 既然和划出屏幕有关系,那么可以认为:
listView的Item回收机制造成的影响。 - 可能是
ViewPager在划出划回的状态有问题。
排查阶段
先看看
ViewPager的OnPageChangeListener在正常与异常情况下切换时回调的状态。发现在onPageScrollStateChanged的回调中,异常状态少了IDLE(0) 和SETTLING(2),只有DRAGGING(1)。查看源码
IDLE(0) 只在smoothScrollTo方法中设置过此状态,依次上溯,发现在setCurrentItemInternal方法中有代码块:
|
|
- 很大概率是此代码块的
if else语句造成的,换言之就是mFirstLayout变量造成的。打个断点试试看! 哇,果然是 当划出屏幕后再回来 走的是
mFirstLayout=true,而正常情况下则是走的else方法。接下来看看
mFirstLayout在哪被赋值过。
|
|
mFirstLayout在上述方法中赋值过,那么问题就显而易见了。
原因:当该itemView被划出屏幕时,会被detach掉,再划回来则会走onAttachedToWindow方法,此时mFirstLayout被设置为true,所以在划回来,第一次切换时会造成前文提到的情况。
解决方案也随之出现(回字有几种写法?):
- 设置
viewpager的OnAttachStateChangeListener侦听,在onViewAttachedToWindow回调中,调用ViewPager.requestLayout();方法将mFirstLayout置为false。(对应onLayout) - 利用反射,在
onViewAttachedToWindow回调中,将mFirstLayout置为false。这个方法有人会有疑问就是 那岂不是在第一次onViewAttachedToWindow的时候mFirstLayout也为false?答案是yes,但是别忘了,setAdapter里会置为true。往往我们第一次onViewAttachedToWindow之后都会给ViewPagersetAdapter啊。所以整体还是对的。 - 投机取巧法。当
itemView划出时调用两次setCurrentItem(currentItem)方法,这样第二次就走else的代码了。但是此方法需要写的代码有点多,而且罗嗦,耦合高,我压根就没试。
方法一:
|
|
方法二:
|
|
resetFirstLayout的主要语句为:
|
|
mFirstLayout.setAccessible(true);mFirstLayout.set(viewPager, false);
(这两句加到代码块生成不了html,蛋疼,拿出来意会就好)
好了,就这么多吧。。