StaggeredGridLayoutManager返回错误的verticalScrollOffset

时间:2015-04-22 17:06:54

标签: android android-recyclerview

我做的几乎与此处(https://github.com/kmshack/Android-ParallaxHeaderViewPager)相同,但在选项卡中使用了RecyclerView。

在一个标签中的RecyclerView具有较大高度的项目,并且第一个在标题下并不总是完全可见。 在某些时候,我调用RecyclerView的computeVerticalScrollOffset()(调用StaggeredGridLayoutManager的方法),它返回完全错误的值。如果我更改标题高度以使第一个项目完全可见我正在获得正确的值。

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

P.S。我也使用LinearLayoutManager并始终获得正确的值,即使第一项在标题

下不完全可见

2 个答案:

答案 0 :(得分:2)

我认为错误在于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));

答案 1 :(得分:0)

让我们按照RecyclerView

的源代码进行操作
@Override
public int computeVerticalScrollOffset() {
    return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
}

mLayout是LayoutManager的一个实例。在我们的例子中,它应该属于StaggeredGridLayoutManager。我们来看看:

@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)));
}

我们应该关注最后的陈述。此方法返回由* avgHeight项计算的偏移量,当我们的项目具有各种高度时,这是不准确的。

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

P.S 我知道它不正确的原因,但我不知道如何获得正确的滚动距离。如果有人知道,请访问:How to get correct scroll distance of RecyclerView when using a StaggeredGridLayoutManager?并发布您的回答。