对程序猿来说最痛苦的事莫过于本来以为很简单的事(当然是指没采过这个坑,以为Google爸爸会帮我们全搞定)事实上费了九牛二虎之力才找到问题并解决,所以任何事情我们都必须得放在心上,重视起来,本猿也是最近再做一个迭代的时候才深深感悟到这个道理的。
迭代的需求:做一个类似于头条的东西,三个地方会有展示人口,三个地方数据必须保持一致.在细一点就是,我当前选择的这个频道下看到哪个位置从不同的地方进去,必须回到那个频道下的那个位置。这时候无非就是将我们上次阅读的频道和对应的数据及与阅读到的位置记录下来。记录阅读位置,使用的方法是:findFirstCompletelyVisibleItemPosition()方法,这时候,测试的小姐姐就发现我问题了,有时候位置会恢复,有时候位置不会恢复,反而恢复到之前的位置,然后呢作者就将保留时取到的位置打印出来看了一下,发现当你的Recycler View的item内容超过一屏时,findFirstCompletelyVisibleItemPosition()会返回-1,于是错误就这样发生了.正所谓源码面前无所遁形,于是,我们就去看看Google是怎么搞事情的.
public int findFirstCompletelyVisibleItemPosition() {
final View child = findOneVisibleChild(0, getChildCount(), true, false);
return child == null ? NO_POSITION : getPosition(child);
}
public static final int NO_POSITION = -1;
果不其然,当findOneVisibleChild()方法返回的child为null时,findFirstCompletelyVisibleItemPosition会返回-1,继续看findOneVisibleChild:
View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible,
boolean acceptPartiallyVisible) {
ensureLayoutState();
final int start = mOrientationHelper.getStartAfterPadding();
final int end = mOrientationHelper.getEndAfterPadding();
final int next = toIndex > fromIndex ? 1 : -1;
View partiallyVisible = null;
for (int i = fromIndex; i != toIndex; i+=next) {
//找到item
final View child = getChildAt(i);
//返回view的开始位置,不包含他的margin和装饰
final int childStart = mOrientationHelper.getDecoratedStart(child);
final int childEnd = mOrientationHelper.getDecoratedEnd(child);
//child的位置在RecyclerView的范围之内
if (childStart < end && childEnd > start) {
if (completelyVisible) {
//当你的Item超出Recycler View高度时,明显不成立
if (childStart >= start && childEnd <= end) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
} else {
return child;
}
}
}
return partiallyVisible;
}
返回布局即RecycvlerView开始和结束的位置
/**
* Returns the start position of the layout after the start padding is added.
*
* @return The very first pixel we can draw.
*/
public abstract int getStartAfterPadding();
/**
* Returns the end position of the layout after the end padding is removed.
*
* @return The end boundary for this layout.
*/
public abstract int getEndAfterPadding();
返回可见的item的数量
//RecyclerView的getChildCount
* Return the current number of child views attached to the parent RecyclerView.
* This does not include child views that were temporarily detached and/or scrapped.
*
* @return Number of attached children
*/
public int getChildCount() {
return mChildHelper != null ? mChildHelper.getChildCount() : 0;
}
//ChildHelp的getChildCount
* Returns the number of children that are not hidden.
*
* @return Number of children that are not hidden.
* @see #getChildAt(int)
*/
int getChildCount() {
return mCallback.getChildCount() - mHiddenViews.size();
}
经过以上对findOneVisibleChild()方法的深入分析可得:
1.当Item高度大于RecyclerView高度时,findFirstCompletelyVisibleItemPosition方法传入的参数使得findOneVisibleChild()方法中定义的View:partiallyVisible没有被赋值返回时仍为null
public int findFirstCompletelyVisibleItemPosition() {
final View child = findOneVisibleChild(0, getChildCount(), true, false);
return child == null ? NO_POSITION : getPosition(child);
}
//findOneVisibleChild方法中代码
if (completelyVisible) {
//当你的Item超出Recycler View高度时,此时下方条件均不成立,
if (childStart >= start && childEnd <= end) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
2.当Item高度大于RecyclerView高度时,findFirstVisibleItemPosition方法传入的参数使得findOneVisibleChild()方法中定义的View:partiallyVisible赋值为child并返回.
public int findFirstVisibleItemPosition() {
final View child = findOneVisibleChild(0, getChildCount(), false, true);
return child == null ? NO_POSITION : getPosition(child);
}
//findOneVisibleChild方法中代码
if (completelyVisible) {
//Item超出RecyclerView高度且acceptPartiallyVisible为true,partiallyVisibler没有被赋值
if (childStart >= start && childEnd <= end) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
3.当Item高度大于RecyclerView高度时,findLastVisibleItemPosition方法传入的参数使得findOneVisibleChild()方法中定义的View:partiallyVisible赋值为child并返回.
public int findLastVisibleItemPosition() {
final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true);
return child == null ? NO_POSITION : getPosition(child);
}
//findOneVisibleChild方法中的for循环执行一次
if (completelyVisible) {
//Item超出RecyclerView高度且acceptPartiallyVisible为false,partiallyVisibler被赋值
if (childStart >= start && childEnd <= end) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
4.当Item高度大于RecyclerView高度时,findLastCompletelyVisibleItemPosition方法传入的参数使得findOneVisibleChild()方法中定义的View:partiallyVisible没有被赋值返回时仍为null
public int findLastCompletelyVisibleItemPosition() {
final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false);
return child == null ? NO_POSITION : getPosition(child);
}
//findOneVisibleChild方法中的for循环执行一次
if (completelyVisible) {
//Item超出RecyclerView高度且acceptPartiallyVisible为true,partiallyVisibler没有被赋值
if (childStart >= start && childEnd <= end) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
只用将以上四个方法和findOneVisibleChild()方法联系起来我们才能识得庐山真面目,得到我们想要的东西!读者可根据注释重点理解findOneVisibleChild方法。