OnScrollListener for RecyclerView's full scroll list is imprecise?

I have implemented OnScrollListener for RecyclerView like this:

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

      

This should add up all of the scroll changes so that scrollY holds the full vertical scroll of the RecyclerView.

But when I scroll down to the end of the list and scroll back, the scroll value is not 0, but positive (in my test case, it ends at about 300).

Which part of my implementation is wrong?

+3


source to share


1 answer


Ok, I figured it out, and it's actually a matter of time. For those who may have the same problem as me at some point, here's what happened:

In my fragment, I added an OnScrollListener to add all the dy's to get the full scrollY value for the RecyclerView:

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

      

In my RecyclerView lines, I wanted to use this mScrollY value each time the RecyclerView scrolls to respond to the parallax effect, so I have implemented an OnScrollListener in the ViewHolder like:

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 OnScrollListener
    }
};

      

I figured that since the ViewHolders were destroyed and reused, it would be most resource efficient to only add child OnScrollListeners when needed.



What I didn't know about the RecyclerView is that its OnScrollListeners are processed in the order of addition (this is obvious after the fact). Since the OnScrollListener fragment was added first, and after the OnScrollListeners line, the OnScrollListener fragments were called last, causing the mScrollY value to update after it was processed in the OnScrollListeners line, so they skipped the last update and stopped from the second to the last mScrollY which was positive.

I fixed it by doing all the logic in the OnScrollListener snippet like everything else (note that I only check the first child, since I only need the parallax effect on the first line! If you have more effects elsewhere you should loop through everything current views in 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);
        }
    }
};

      

And method injection into ViewHolders:

public void dispatchScrollY(int scrollY){
    // handle parallax effect here
}

      

Now it works.

+1


source







All Articles