web-dev-qa-db-fra.com

RecyclerView scrollToPosition ne déclenche pas scrollListener

J'utilise RecyclerView, avec ScrollListener:

mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener()
{
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState)
        {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            super.onScrolled(recyclerView, dx, dy);
            // Do my logic
        }
 });

Lorsque je fais défiler avec le doigt, le listener de défilement s'est bien déclenché.

Mais quand je fais défiler progressivement, comme ça:

mRecyclerView.scrollToPosition(LAST_POSITION);

Le listener de défilement n'est pas déclenché.

23
David

C'est un problème connu. Cela est dû au fait que RecyclerView ne sait pas comment LayoutManager gérera le défilement ou s’il le fera.

Dans la prochaine version, vous recevrez un appel à onScrolled si la première et/ou la dernière position enfant change après une présentation (ce qui est généralement le résultat d'un appel de scroll à position).

Malheureusement, dx et dy seront égaux à 0 car RecyclerView ne sait pas vraiment combien de gestionnaires de disposition ont fait défiler pour traiter la demande scrollTo. Vous pouvez également utiliser le rappel de défilement du ViewTreeObserver.

22
yigit

J'ai rencontré le même problème et trouvé une solution de contournement consistant à utiliser le paramètre smoothScrollToPosition dans le gestionnaire de disposition. Cette méthode déclenche l'écouteur de défilement. Donc, fondamentalement, voici ce que j’avais l'habitude d'avoir:

recyclerView.scrollToPosition(recyclerAdapter.getItemCount() - 1);

et maintenant j'ai changé en ceci:

recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, recyclerAdapter.getItemCount() - 1);

et ça marche bien pour moi.

22
luis.mazoni

C'est un peu rudimentaire, mais vous pouvez essayer cette sous-classe de RecyclerView:

public class PatchedRecyclerView extends RecyclerView {

    private OnScrollListener listener;

    public PatchedRecyclerView(Context context) {
        super(context);
    }

    public PatchedRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PatchedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setOnScrollListener(OnScrollListener listener) {
        super.setOnScrollListener(listener);
        this.listener = listener;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        int preFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
        int preLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
        super.onLayout(changed, l, t, r, b);
        int postFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
        int postLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;

        // TODO: Insert proper DX and DY values
        if (preFirstChildPosition != postFirstChildPosition || preLastChildPosition != postLastChildPosition)
            listener.onScrolled(this, 0, 0);
    }
}
0
Alex Hart

Pour déclencher explicitement onScrollListener sur la vue Recycler, utilisez:

recyclerView.smoothScrollBy(x, y);
//x is the horizontal displacement and y is vertical displacement.
0
saurabh dhillon
int mFirst=0, mLast=0;

recyclerview.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    }
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
        mLast = llm.findLastCompletelyVisibleItemPosition();
        mFirst = llm.findFirstCompletelyVisibleItemPosition();
    }
});

imgRight.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
        llm.scrollToPositionWithOffset(mLast + 1, List.length());
    }
});

imgLeft.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
        llm.scrollToPositionWithOffset(mFirst - 1, List.length());
    }
});
0
Rushikesh Patil

S'il vous plaît essayer avec 

LinearLayoutManager.scrollToPositionWithOffset(int, int).

LayoutManager.scrollToPosition() peut ne pas fonctionner correctement, mais LinearLayoutManager.scrollToPositionWithOffset() fonctionnera. Et pareillement, GridLayoutManager.

Sinon, nous devons écrire notre méthode d'implémentation personnalisée car RecyclerView ne sait pas comment LayoutManager fait défiler la vue.

    class CustomLayoutManager extends LinearLayoutManager{
         public void smoothScrollToPosition(RecyclerView recyclerView,
                RecyclerView.State state, final int position) {

          LinearSmoothScroller smoothScroller = 
                    new LinearSmoothScroller(mContext) {
        //Override the methods and write your implementation as per your requirement 
       //one which controls the direction and another one calculate the speed per Pixel
        }
    }

Parfois cela fait l'affaire

    new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        yourList.scrollToPosition(position);
    }
}, 200);
0

Je ne pouvais pas utiliser la fonction smoothScrollToPosition (nous avions quelques problèmes par le passé) et, en fonction de la position du premier élément, ne paraissait pas fiable, j'ai donc utilisé la réponse de yigit (elle a déjà été publiée), Malheureusement, cela n'a pas toujours fonctionné (je ne sais pas pourquoi). 

Je me suis donc retrouvé à utiliser un écouteur de défilement que j'avais déjà ajouté à RecyclerView. Malheureusement, je ne trouve pas l'origine de cette solution.
Je ne pense pas que vous devriez utiliser cet auditeur si vous en avez besoin, mais heureusement, je n'ai besoin d'écouter qu'à des moments précis. 

Remplacer le RecyclerView: 

public class MyRecyclerView extends RecyclerView {

    public interface OnScrollStoppedListener{
        void onScrollStopped();
    }

    private static final int NEW_CHECK_INTERVAL = 100;
    private OnScrollStoppedListener mOnScrollStoppedListener;
    private Runnable mScrollerTask;
    private int mInitialPosition;

    public MyRecyclerView(Context context) {
        super(context);
        init();
    }

    public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mScrollerTask = new Runnable() {
            public void run() {
                int newPosition = getScrollY();
                if(mInitialPosition - newPosition == 0) {//has stopped
                    if(mOnScrollStoppedListener !=null) {
                        mOnScrollStoppedListener.onScrollStopped();
                    }
                } else {
                    startScrollerTask();
                }
            }
        };
    }

    public void setOnScrollStoppedListener(MyRecyclerView.OnScrollStoppedListener listener){
        mOnScrollStoppedListener = listener;
    }

    public void startScrollerTask() {
        mInitialPosition = getScrollY();
        postDelayed(mScrollerTask, NEW_CHECK_INTERVAL);
    }

}  

Usage: 

myRecyclerView.setOnScrollStoppedListener(new MyRecyclerView.OnScrollStoppedListener() {
    @Override
    public void onScrollStopped() {
        // Do your thing
    }
});

myRecyclerView.startScrollerTask();
0
MikeL