RecyclerView刷新后定位问题_android recyclerview刷新后定位原来的位置

        updateLayoutStateToFillStart(mAnchorInfo);
        mLayoutState.mExtraFillSpace = extraForStart;
        mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
        //关键步骤3,从锚点View位置向前填充
        fill(recycler, mLayoutState, state, false);
        startOffset = mLayoutState.mOffset;

        if (mLayoutState.mAvailable > 0) {
        //如果锚点View位置前面数据不足,那把剩余空间加到尾部再做一次尝试
            extraForEnd = mLayoutState.mAvailable;
            // start could not consume all it should. add more items towards end
            updateLayoutStateToFillEnd(lastElement, endOffset);
            mLayoutState.mExtraFillSpace = extraForEnd;
            fill(recycler, mLayoutState, state, false);
            endOffset = mLayoutState.mOffset;
        }

}


先解释一下锚点View,锚点View在一次layout过程中的位置不会发生变化,即之前在哪里显示,这次layout完还在哪,从视觉上看没有位移。


总结一下,mLayout.onLayoutChildren主要做了以下几件事:


1. 调用updateAnchorInfoForLayout方法确定锚点view位置
2. 从锚点view后面的位置开始填充,直到后面空间被填满或者已经遍历到最后一个itemView
3. 从锚点view前面的位置开始填充,直到空间被填满或者遍历到indexe为0的itemView
4. 经过第三步后仍有剩余空间,则把剩余空间加到尾部再做一次尝试


所以回到一开始的问题,RecyclerView在notify之后位置跳跃的关键在于锚点View的确定,也就是`updateAnchorInfoForLayout`方法,所以下面重点看下这个方法:



private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state,
AnchorInfo anchorInfo) {
if (updateAnchorFromPendingData(state, anchorInfo)) {
if (DEBUG) {
Log.d(TAG, “updated anchor info from pending information”);
}
return;
}

if (updateAnchorFromChildren(recycler, state, anchorInfo)) {
    if (DEBUG) {
        Log.d(TAG, "updated anchor info from existing children");
    }
    return;
}
if (DEBUG) {
    Log.d(TAG, "deciding anchor info for fresh state");
}
anchorInfo.assignCoordinateFromPadding();
anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;

}


这个方法比较短,所以代码全贴出来了。如果是调用了scrollToPosition后的刷新,会通过updateAnchorFromPendingData方法确定锚点View位置,否则调用`updateAnchorFromChildren`来计算:



private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler,
RecyclerView.State state, AnchorInfo anchorInfo) {
if (getChildCount() == 0) {
return false;
}
final View focused = getFocusedChild();
if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) {
anchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));
return true;
}
if (mLastStackFromEnd != mStackFromEnd) {
return false;
}
View referenceChild =
findReferenceChild(
recycler,
state,
anchorInfo.mLayoutFromEnd,
mStackFromEnd);
if (referenceChild != null) {
anchorInfo.assignFromView(referenceChild, getPosition(referenceChild));

return true;
}
return false;
}


代码比较简单,如果有焦点View,并且焦点View没被remove,则使用焦点View作为锚点。否则调用`findReferenceChild`来查找:



View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,
boolean layoutFromEnd, boolean traverseChildrenInReverseOrder) {
ensureLayoutState();

// Determine which direction through the view children we are going iterate.
int start = 0;
int end = getChildCount();
int diff = 1;
if (traverseChildrenInReverseOrder) {
    start = getChildCount() - 1;
    end = -1;
    diff = -1;
}

int itemCount = state.getItemCount();

final int boundsStart = mOrientationHelper.getStartAfterPadding();
final int boundsEnd = mOrientationHelper.getEndAfterPadding();

View invalidMatch = null;
View bestFirstFind = null;
View bestSecondFind = null;

for (int i = start; i != end; i += diff) {
    final View view = getChildAt(i);
    final int position = getPosition(view);
    final int childStart = mOrientationHelper.getDecoratedStart(view);
    final int childEnd = mOrientationHelper.getDecoratedEnd(view);
    if (position >= 0 && position < itemCount) {
        if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
            if (invalidMatch == null) {
                invalidMatch = view; // removed item, least preferred
            }
        } else {
            // b/148869110: usually if childStart >= boundsEnd the child is out of
            // bounds, except if the child is 0 pixels!
            boolean outOfBoundsBefore = childEnd <= boundsStart && childStart < boundsStart;
            boolean outOfBoundsAfter = childStart >= boundsEnd && childEnd > boundsEnd;
            if (outOfBoundsBefore || outOfBoundsAfter) {
                // The item is out of bounds.
                // We want to find the items closest to the in bounds items and because we
                // are always going through the items linearly, the 2 items we want are the
                // last out of bounds item on the side we start searching on, and the first
                // out of bounds item on the side we are ending on.  The side that we are
                // ending on ultimately takes priority because we want items later in the
                // layout to move forward if no in bounds anchors are found.
                if (layoutFromEnd) {
                    if (outOfBoundsAfter) {
                        bestFirstFind = view;
                    } else if (bestSecondFind == null) {
                        bestSecondFind = view;
                    }
                } else {
                    if (outOfBoundsBefore) {
                        bestFirstFind = view;
                    } else if (bestSecondFind == null) {
                        bestSecondFind = view;
                    }
                }
            } else {
                // We found an in bounds item, greedily return it.
                return view;
            }
        }
    }
}

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

欢迎大家一起交流讨论啊~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值