web-dev-qa-db-ja.com

RecyclerView scrollToPositionはscrollListenerをトリガーしません

私はScrollListenerでRecyclerViewを使用しています:

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

指でスクロールすると、スクロールリスナーが正常にトリガーされました。

しかし、そのように前向きにスクロールすると:

mRecyclerView.scrollToPosition(LAST_POSITION);

スクロールリスナーはトリガーされません。

26
David

これは既知の問題です。これは、RecyclerViewがLayoutManagerがスクロールを処理する方法、またはスクロールを処理するかどうかを知らないという事実が原因です。

次のリリースでは、レイアウトの後に最初および/または最後の子の位置が変更された場合にonScrolledの呼び出しを受け取ります(これは通常、位置へのスクロールの呼び出しの結果です)。

残念ながら、RecyclerViewはscrollToリクエストを処理するためにどのくらいのレイアウトマネージャーがスクロールしたか実際にはわからないため、dxとdyは0になります。または、ViewTreeObserverのスクロールコールバックを使用することもできます。

24
yigit

私は同じ問題に直面し、回避策を見つけました。これは、レイアウトマネージャーでsmoothScrollToPositionを使用することです。このメソッドは、スクロールリスナーをトリガーします。だから基本的にこれは私が持っているものです:

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

そして今、私はこれに変更しました:

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

それは私にとってはうまく機能しています。

24
luis.mazoni

ムイト・リーガル・セウ・コンセルホ。

 Button maisOpcoes = (Button) findViewById(R.id.maisOpcoes);
        maisOpcoes.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null , recyclerViewTmd.getItemCount() - 1);
                maisOpcoes.setVisibility(View.GONE);
            }
        });
0
George Freire

少し初歩的ですが、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

リサイクラビューでonScrollListenerを明示的にトリガーするには、次を使用します。

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

してみてください

_LinearLayoutManager.scrollToPositionWithOffset(int, int).
_

LayoutManager.scrollToPosition()はうまく機能しないかもしれませんが、LinearLayoutManager.scrollToPositionWithOffset()は機能します。同様に、GridLayoutManagerも同様です。

または、RecyclerViewはLayoutManagerがビューをスクロールする方法を知らないため、カスタムの実装方法を記述する必要があります。

_    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
        }
    }
_

時々これはトリックをします

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

SmoothScrollToPositionを使用できませんでした(過去にいくつかの問題が発生していました)。最初のアイテムの位置によっては信頼できないように見えたため、 yigit の答え(既にリリースされています)、残念ながら常に機能しませんでした(理由はわかりません)。

そのため、以前にRecyclerViewに追加したスクロールリスナーを使用することになりました。残念ながら、このソリューションの起源は見つかりません。
一定のリスナーが必要な場合、このリスナーを使用する必要はないと思います。幸いなことに、特定の時間にのみ聞く必要があります。

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

}  

使用法:

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

myRecyclerView.startScrollerTask();
0
MikeL