RecyclerView的OnScrollListener总滚动距离不准确?

3

我为RecyclerView实现了一个OnScrollListener,如下所示:

new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        scrollY += dy;
    }
};

这应该累加所有滚动更改,以便scrollY保存RecyclerView的总垂直滚动。

但是当我滚动到列表末尾并向上滚动时,滚动值不等于0,而是为正数(在我的测试示例中,它最终达到300左右)。

我的实现哪里出了问题?


是否开启了“overScroll”模式? - anil
我没有改变overScroll,所以它是默认值(OVER_SCROLL_ALWAYS)。我怀疑这可能与ViewPager中的片段重新实例化有关,因为Recycler位于ViewPager内部的片段中,我现在将仔细检查我的片段状态恢复。OverScroll和OnScrollListener是否存在已知问题? - A. Steenbergen
我排除了那个问题,因为我的片段从未被保存。我已经怀疑过了,因为我使用的是FragmentPagerAdapter而不是FragmentStatePagerAdapter,但我刚刚通过Logcat进行了双重检查,这与片段重新实例化无关。 - A. Steenbergen
我仍然遇到你在问题中首次描述的问题。看起来依赖于onScrolled回调函数无法得到准确的滚动总量... 如果你拖动列表来回滑动而不松开手指,一切都运行良好,但一旦你甩动列表,该值与实际应该的值不同步。我还没有解决这个问题。 - brAzzi64
@brAzzi64 是的,我也想到了同样的方法。但是如果你正在使用LinearLayoutManager,有一种更准确的计算方法。你可以使用它来获取当前屏幕上的视图,然后调用其中一个视图的getTop()方法,就可以得到视图的非常精确的位置。 - A. Steenbergen
1个回答

1

好的,我已经理解了,这实际上是一个时间问题。对于那些可能在某个时候遇到与我相同问题的人,这是发生的情况:

在我的片段中,我添加了一个OnScrollListener来累加所有dy值,以获取RecyclerView的完整scrollY值:

new RecyclerView.OnScrollListener() {
@Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        mScrollY += dy;
    }
};

在我的RecyclerView行中,我想使用这个mScrollY值,在RecyclerView滚动时,以视差效果做出响应,因此我在ViewHolder中实现了一个OnScrollListener:

new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        applyParallax(mScrollY); //mScrollY is calculated by fragment's OnScrollListener
    }
};

我认为,由于ViewHolders被销毁并重用,只在需要时添加子OnScrollListeners是最节约资源的。

我不知道关于RecyclerView的一件事是它的OnScrollListeners按添加顺序处理(这在事后有点显而易见)。由于片段的OnScrollListener首先被添加,而行的OnScrollListeners之后,因此片段的OnScrollListener最后被调用,导致mScrollY值在被行的OnScrollListeners处理后更新,因此它们错过了最后一次更新并停留在倒数第二个mScrollY值,该值为正数。

我通过像这样将所有逻辑实现到片段的OnScrollListener中来解决它(请注意,由于我只需要第一行中的视差效果,因此仅检查第一个子项!如果您在其他位置具有更多效果,则必须循环遍历LayoutManager中的所有当前视图):

new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        mScrollY += dy;
        LinearLayoutManager manager = ((LinearLayoutManager) mRecyclerView.getLayoutManager());
        View firstChild = manager.getChildAt(0);
        if(firstChild.getId() == R.id.fpd_title_super){
            final PostDataAdapter.MainTitleViewHolder holder = (PostDataAdapter.MainTitleViewHolder)mRecyclerView.getChildViewHolder(firstChild);
            holder.dispatchScrollY(mScrollY);
        }
    }
};

在ViewHolders中实现一个方法:
public void dispatchScrollY(int scrollY){
    // handle parallax effect here
}

现在它可以工作了。

1
如果您仍然遇到滚动和视差效果的问题,可以查看此实现:https://github.com/kanytu/android-parallax-recyclerview - Pedro Oliveira
这是一个不错的库,我暂时不会使用它,因为我想尽可能地坚持使用框架API,但这可能对其他遇到此问题的人有所帮助! - A. Steenbergen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接