【问题标题】:RecyclerView.OnScrollListener: being called multiple times for once instance of scrollRecyclerView.OnScrollListener:一次滚动实例被多次调用
【发布时间】:2018-03-07 12:02:43
【问题描述】:

我有一个水平布局的回收视图,一次只能看到一个视图:

mRecyclerView = findViewById(R.id.rvmain);
mRecyclerView.setOnFlingListener(null);
final SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(mRecyclerView);
mLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new MainActivityRVAdapter(postsModels,MainActivity.this);
mRecyclerView.setAdapter(mAdapter);

使用 onScrolllistener,每次滚动我都想知道开始位置和结束位置。我正在使用以下代码:

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);


        if(count == 0) {
            View centerView = snapHelper.findSnapView(mLayoutManager);
            if(centerView != null){
                initial_position = mLayoutManager.getPosition(centerView);
                //initial_position2 = ((LinearLayoutManager)mRecyclerView.getLayoutManager()).findFirstVisibleItemPosition();
                Log.e("Initial Item Position:",""+initial_position);
                //Log.e("Initial Item Position2:",""+initial_position2);
            }
            count ++;
        }



        // get newstate position
        if(newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
            View centerView = snapHelper.findSnapView(mLayoutManager);
            if(centerView != null){
                int pos = mLayoutManager.getPosition(centerView);

                count = 0; // in idle state clear the count again
                Log.e("Snapped Item Position:",""+pos);
            }
        }


    }

我得到的结果是:

E/Initial Item Position:: 0
E/Snapped Item Position:: 1
E/Initial Item Position:: 1
E/Snapped Item Position:: 1
E/Initial Item Position:: 1
E/Snapped Item Position:: 1
E/Initial Item Position:: 1
E/Snapped Item Position:: 1

它会多次返回位置。我想检查最终位置和初始位置之间的差异。 我只想要开始和结束,以便我可以比较和检查,即:

E/Initial Item Position:: 0 and

E/Snapped Item Position:: 1

【问题讨论】:

    标签: android android-recyclerview


    【解决方案1】:

    我遇到了同样的问题。我发现了什么: RecyclerView.OnScrollListenerSCROLL_STATE_DRAGGINGSCROLL_STATE_SETTLING 中多次调用onScrolled(...)

    我开始听第二个回调:
    onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState)
    我们对最终状态很感兴趣SCROLL_STATE_IDLE

    所以在这种情况下,我们必须覆盖onScrollStateChanged(...),检查并忽略除SCROLL_STATE_IDLE 之外的所有状态,并在空闲时获得最终位置。

    但如docsonScrolled(...)中所述

    如果可见项目范围在布局后发生变化,也会被调用 计算。在这种情况下,dx 和 dy 将为 0。

    在实践中我发现如果我们调用adapter.notifyDataSetChanged() 并且可见位置是“0”(即在第一次启动时),onScrollStateChanged(...) 将不会被调用,onScrolled(...) 将被调用一次与 dx == dy == 0

    最终的变体可能如下:

    private int recyclerVisiblePosition;    
    @Override
    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
       super.onScrollStateChanged(recyclerView, newState);
    
       if (newState != RecyclerView.SCROLL_STATE_IDLE) {
          return;
       }
    
       getNewPosition(recyclerView);
    }
    
    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
    
        if (dx == 0 && dy == 0) {
            getNewPosition(recyclerView);
        } 
    }
    
    private void getNewPosition(@NonNull final RecyclerView recyclerView) {
        final LinearLayoutManager layoutManager = ((LinearLayoutManager)recyclerView.getLayoutManager());
        if (layoutManager != null) {
            recyclerVisiblePosition = layoutManager.findLastVisibleItemPosition();
        }
    }
    

    【讨论】:

      【解决方案2】:

      原因:

      当你在RecyclerView上滚动时,它最终会触发SCROLL_STATE_IDLE事件,这是由于你扔的动作,然后你松开RecyclerView

      SnapHelper 将侦听RecyclerView 的第一个SCROLL_STATE_IDLE 事件,以便它可以准备计算IT 需要滚动到目标View 的剩余距离,当它这样做了,它会第二次触发SCROLL_STATE_IDLE 事件,因为SnapHelper 正在“帮助”你滚动它。

      ================================================ ================

      解决方案:

      您可以创建一个全局变量并将其用作“标志”,这样可以避免它被多次调用。最终代码将如下所示:

      private boolean isFirstTimeCall = true;
      
      recyclerViewExample.addOnScrollListener(new RecyclerView.OnScrollListener() {
              @Override
              public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                  super.onScrollStateChanged(recyclerView, newState);
      
                  if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                      if (isFirstTimeCall) {
                          isFirstTimeCall = false;
      
                          // Do your stuff here...
                      }
                  }
      
                  if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                      isFirstTimeCall = true;
                  }
              }
          });
      

      【讨论】:

        【解决方案3】:

        我遇到了同样的问题,我意识到这是导致问题的 snap 助手。我想这是因为 snap helper 在你的滚动完成后触发了一个小的第二个动作。所以 onScrollStateChanged 被调用了两次。

        幸运的是,现在有了 ViewPager2。所以我不必为了听啪啪声而发疯。因为我像你一样一次只有一个可见视图,所以我用 ViewPager2 替换了 Recyclerview,而不是 Recyclerview.OnScrollListener,我使用了 ViewPager2.OnPageChangeCallback。这样就解决了问题。

        【讨论】:

          猜你喜欢
          • 2016-07-04
          • 1970-01-01
          • 2015-10-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多