RecyclerView的绘制逻辑主要由LayoutManager控制。onLayoutChildren是最主要的布局方法。
其中首先做的是查找锚点,updateAnchorInfoForLayout方法,锚点是布局的起点,主要有两个属性很关键,mLayoutFromEnd这个是控制布局方式的,从上到下还是从下到上,默认是从上到下的;mPosition记录锚点使用的data position,即使用哪条数据作为锚点。
锚点查找主要分两种查找逻辑,优先updateAnchorFromPendingData,然后才是updateAnchorFromChildren。
updateAnchorFromPendingData主要判断逻辑是否存在合理的mPendingScrollPosition(这个值存储的是adapter的data position),如果存在,就会将其赋值给锚点mPosition,同时对锚点的其他参数赋值;
如果updateAnchorFromPendingData失败,就会走updateAnchorFromChildren逻辑,这个逻辑也很简单,就是从当前显示的view中找锚点,按照锚点的布局方向,找最靠近起点的view作为锚点。具体逻辑见findReferenceChild方法。
RecyclerView会在绘制完成后,onLayoutCompleted方法,将锚点、mPendingSavedState、mPendingScrollPosition等信息清空,所以当发起一次新的layout时,就需要重新寻找锚点。
这种逻辑遇到的问题就是当展示在第一位的item在数据更新后被重新排序插到了其他位置,这个时候layout结束后RecyclerView滚动到了这个item所在的位置。有上面的锚点查找逻辑,这就很容易理解了,这个itemview因为最靠前,被作为锚点了,因为锚点查找是通过当前显示的view反向找data position,就导致了RecyclerView滚动。
解决的办法是使用LayoutManager的onSaveInstanceState和onRestoreInstanceState方法,其实就是手动设置锚点信息,将layout之前的锚点信息设置回去,走updateAnchorFromPendingData逻辑,避免updateAnchorFromChildren查找锚点。