Android StaggedGridLayoutManager返回错误的垂直滚动偏移

Android StaggedGridLayoutManager返回错误的垂直滚动偏移,android,android-recyclerview,Android,Android Recyclerview,我所做的几乎与此处()相同,但在选项卡中使用了RecyclerView 一个选项卡中的RecyclerView包含高度较大的项目,第一个项目在标题下并不总是完全可见。 在某个时候,我调用了RecyclerView的computeVerticalScrollOffset()(它调用了StaggedGridLayoutManager的方法),它返回了完全错误的值。若我改变页眉高度以使第一项完全可见,那个么我得到的值是正确的 是否有任何已知的解决方案/修复方案 另外,我还使用LinearLayoutM

我所做的几乎与此处()相同,但在选项卡中使用了RecyclerView

一个选项卡中的RecyclerView包含高度较大的项目,第一个项目在标题下并不总是完全可见。 在某个时候,我调用了RecyclerView的computeVerticalScrollOffset()(它调用了StaggedGridLayoutManager的方法),它返回了完全错误的值。若我改变页眉高度以使第一项完全可见,那个么我得到的值是正确的

是否有任何已知的解决方案/修复方案


另外,我还使用LinearLayoutManager,即使标题下的第一项不完全可见,也始终可以获得正确的值

@Override
public int computeVerticalScrollOffset() {
    return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
}
mLayout是
LayoutManager
的一个实例。在我们的例子中,它应该属于
StaggedGridLayoutManager
。让我们看看那里:

@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
    return computeScrollOffset(state);
}
那么这里:

private int computeScrollOffset(RecyclerView.State state) {
    if (getChildCount() == 0) {
        return 0;
    }
    ensureOrientationHelper();
    return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation,
            findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true)
            , findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true),
            this, mSmoothScrollbarEnabled, mShouldReverseLayout);
}
最后我们转到ScrollbarHelper.computeScrollOffset:

/**
 * @param startChild View closest to start of the list. (top or left)
 * @param endChild   View closest to end of the list (bottom or right)
 */
static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
        View startChild, View endChild, RecyclerView.LayoutManager lm,
        boolean smoothScrollbarEnabled, boolean reverseLayout) {
    if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
            endChild == null) {
        return 0;
    }
    final int minPosition = Math.min(lm.getPosition(startChild),
            lm.getPosition(endChild));
    final int maxPosition = Math.max(lm.getPosition(startChild),
            lm.getPosition(endChild));
    final int itemsBefore = reverseLayout
            ? Math.max(0, state.getItemCount() - maxPosition - 1)
            : Math.max(0, minPosition);
    if (!smoothScrollbarEnabled) {
        return itemsBefore;
    }
    final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
            orientation.getDecoratedStart(startChild));
    final int itemRange = Math.abs(lm.getPosition(startChild) -
            lm.getPosition(endChild)) + 1;
    final float avgSizePerRow = (float) laidOutArea / itemRange;

    return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
            - orientation.getDecoratedStart(startChild)));
}
我们应该关注最后一句话。此方法返回由items*avgHeight计算的偏移量,当我们的项目具有不同的高度时,该值不准确

因此,当我们使用
GridLayoutManager
LinearLayoutManager
时,由于确认了每行的高度,
computeVerticalScrollOffset()
将返回正确的距离。然而,不幸的是,当我们使用
StaggedGridLayoutManager
时,我们无法通过它获得准确的滚动偏移量


p.S我知道它不正确的原因,但我不知道如何获得正确的滚动距离。如果有人知道,请访问:并发布您的答案

我认为错误在于computeScrollOffset()函数参数中使用的findFirstVisiblePosition()和findLastVisiblePosition()。即使我使用几乎相等的行高,computeVerticalScrollOffset()也会返回非常奇怪的数字。 如果我们用实际有效的recyclerView.getChildAt(0)和recyclerView.getChildAt(recyclerView.getChildCount()-1)替换它们

因此,我们可以编写自己的函数:

View firstItemView = recyclerView.getChildAt(0);
View lastItemView = recyclerView.getChildAt(recyclerView.getChildCount() - 1);

int firstItem = recyclerView.getChildLayoutPosition(firstItemView);
int lastItem = recyclerView.getChildLayoutPosition(lastItemView);
int itemsBefore = firstItem;

int laidOutArea = recyclerView.getLayoutManager().getDecoratedBottom(lastItemView) - recyclerView.getLayoutManager().getDecoratedTop(firstItemView);
int itemRange = lastItem - firstItem + 1;
float avgSizePerRow = (float) laidOutArea / itemRange;

int offset = (int) (itemsBefore * avgSizePerRow + recyclerView.getLayoutManager().getPaddingTop() - recyclerView.getLayoutManager().getDecoratedTop(firstItemView));