【问题标题】:Implementation of onScrollListener to detect the end of scrolling in a ListView实现 onScrollListener 来检测 ListView 中的滚动结束
【发布时间】:2011-09-15 13:13:11
【问题描述】:

我有一个 ListView 显示一些项目。我想对ListView 的可见部分中当前显示的项目执行一些操作,具体取决于ListView 的滚动方式;因此我想实现ListViewOnScrollListener。 根据 Android api 参考,onScroll 方法“将在滚动完成后调用”。在我看来,这正是我所需要的,因为一旦滚动完成,我就会在 ListView 上执行我的操作(onScroll 方法返回显示的第一个项目的索引和显示的项目数)。

但是一旦实现,我从LogCat 中看到,onScroll 方法不仅会在滚动完成后触发,而且会针对进入显示视图的每个新项目触发,从滚动的开始到结束.这不是我期望也不是我需要的行为。侦听器的另一个方法 (onScrollStateChanged) 不提供有关当前显示在 ListView 中的项目的信息。

那么,有谁知道如何使用这两种方法来检测滚动的结束并获取有关显示项目的信息? api 参考和方法的实际行为之间的不一致让我有点困惑。 提前致谢。

P.S.:我见过一些类似的话题,但没有什么能帮助我理解整个事情是如何运作的......!

【问题讨论】:

标签: android listview onscroll


【解决方案1】:

最后,我找到了一个不太优雅但对我有用的解决方案;发现滚动的每一步都会调用 onScroll 方法,而不是仅在滚动结束时调用,并且实际上仅在滚动完成时才调用 onScrollStateChanged ,我会这样做:

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    this.currentFirstVisibleItem = firstVisibleItem;
    this.currentVisibleItemCount = visibleItemCount;
}

public void onScrollStateChanged(AbsListView view, int scrollState) {
    this.currentScrollState = scrollState;
    this.isScrollCompleted();
 }

private void isScrollCompleted() {
    if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) {
        /*** In this way I detect if there's been a scroll which has completed ***/
        /*** do the work! ***/
    }
}

实际上,每次滚动 ListView 时,我都会保存有关第一个可见项和可见项计数的数据(onScroll 方法);当滚动状态发生变化时(onScrollStateChanged),我保存状态并调用另一个方法,该方法实际上了解是否存在滚动以及是否已完成。通过这种方式,我也有关于我需要的可见项目的数据。 也许不干净但有效!

问候

【讨论】:

  • 我使用了类似的东西,在我的情况下效果非常好。简单但有效:public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } public void onScrollStateChanged(AbsListView view, int scrollState) { isScrollCompleted(scrollState); } private void isScrollCompleted(int currentScrollState) { if (currentScrollState == OnScrollListener.SCROLL_STATE_IDLE) { mAdapter.notifyDataSetChanged(); } }
  • 如何一次显示 10 个项目,然后使用您的代码加载另外 10 个项目。请帮忙!!!
  • 不需要currentFirstVisibleItem
  • 非常感谢......它非常优雅
  • currentFirstVisibleItem 可以与 0 比较来检测滚动到顶部。
【解决方案2】:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
  int visibleItemCount, int totalItemCount) {

  if (list.getLastVisiblePosition() == list.getAdapter().getCount() - 1
      && list.getChildAt(list.getChildCount() - 1).getBottom() <= list.getHeight()) {
    // Scroll end reached
    // Write your code here
  }
}

【讨论】:

    【解决方案3】:
    @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
    
                if((firstVisibleItem + visibleItemCount) ==  totalItemCount)
                {
                    Log.i("Info", "Scroll Bottom" );
                }
            }});
    

    【讨论】:

      【解决方案4】:

      要降低这种行为是很棘手的,我花了很长时间才完善。问题的实质是,据我所知,滚动侦听器本身并不足以检测到“滚动停止”(包括方向按钮/轨迹球)。我最终按照我的预期做了一系列正确的事情。

      我认为最好的方法是扩展 ListView 并覆盖一些方法:

        ....
        @Override
        public boolean onKeyUp(int keyCode, KeyEvent event) {
          if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP) {
            startWait();
          }
          return super.onKeyUp(keyCode, event);
        }
      
        @Override
        public boolean onTouchEvent(MotionEvent event) {
          if (event.getAction() == MotionEvent.ACTION_DOWN) {
            stopWait();
          }
          if (event.getAction() == MotionEvent.ACTION_UP ||
              event.getAction() == MotionEvent.ACTION_CANCEL) {
            startWait();
          }
          return super.onTouchEvent(event);
        }
      
        private OnScrollListener customScrollListener = new OnScrollListener() {
          @Override
          public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
            int totalItemCount) {
            stopWait();
          }
      
          @Override
          public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
              startWait();
            } else {
              stopWait();
            }
          }
        };
      
        //All this waiting could be improved, but that's the idea
        private Thread waitThread = null;
        private int waitCount  = Integer.MIN_VALUE;
      
        public void stopWait() {
          waitCount = Integer.MIN_VALUE;
        }
      
        public synchronized void startWait() {
          waitCount = 0;
      
          if (waitThread != null) {
            return;
          }
      
          waitThread = new Thread(new Runnable() {
          @Override
          public void run() {
          try {
            for (; waitCount.get() < 10; waitCount++) {
              Thread.sleep(50);
            }
      
              //Kick it back to the UI thread.
              view.post(theRunnableWithYourOnScrollStopCode); // HERE IS WHERE YOU DO WHATEVER YOU WANTED TO DO ON STOP
            } catch (InterruptedException e) {
            } finally  {
              waitThread = null;
            }
          }
        });
        waitThread.start();
      }
      

      请注意,您还必须在构造函数中绑定customScrollListener。我认为这个实现很好,因为它不会立即触发“事件”,它会等待一段时间,直到它实际上完全停止滚动。

      【讨论】:

      • 谢谢,你的回答对我有很大帮助,但现在对我的目的来说有点太棘手了;我已经结束了我在下面发布的另一种解决方案......
      • @dmon 你的意思是theRunnableWithYourOnScrollStopCode 是我可以把我的功能放在那里的地方:比如加载数据或发出网络请求?谢谢!
      【解决方案5】:

      分析之前的答案,找到了自己的答案:

      在 onViewCreated、onCreate 或任何你获取列表视图的地方:

      ListView list = (ListView) view.findViewById(R.id.[your id]);
          list.setOnScrollListener(this);
      

      然后填写监听方法:

      public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
       // yes, just leave it empty
      }
      
      public void onScrollStateChanged(AbsListView view, int scrollState) {
          this.isScrollCompleted(view, scrollState);
       }
      
      private void isScrollCompleted(AbsListView view, int scrollState) {
          if (!view.canScrollVertically(1) && this.currentScrollState == SCROLL_STATE_IDLE) {
              // do work
          }
      }
      

      似乎做了它应该做的:检测列表的结尾而不需要大量的代码和适配器的修改。

      【讨论】:

        【解决方案6】:

        试试这个……

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                             int totalItemCount) {
        
            int position = firstVisibleItem+visibleItemCount;
            int limit = totalItemCount;
        
            // Check if bottom has been reached
            if (position >= limit && totalItemCount > 0) {
                 //scroll end reached
                  //Write your code here
            }
        }
        

        【讨论】:

          【解决方案7】:

          e-cal's answer 的更有用的表示

          监听器实现:

          public abstract class OnScrollFinishListener implements AbsListView.OnScrollListener {
          
              int mCurrentScrollState;
              int mCurrentVisibleItemCount;
          
              @Override
              public void onScrollStateChanged(AbsListView view, int scrollState) {
                  mCurrentScrollState = scrollState;
                  if (isScrollCompleted()) {
                      onScrollFinished();
                  }
              }
          
              @Override
              public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                  mCurrentVisibleItemCount = visibleItemCount;
              }
          
              private boolean isScrollCompleted() {
                  return mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE;
              }
          
              protected abstract void onScrollFinished();
          }
          

          用法:

              listView.setOnScrollListener(new OnScrollFinishListener() {
                   @Override
                   protected void onScrollFinished() {
                       // do whatever you need here!
                   }
              });
          

          【讨论】:

            【解决方案8】:

            还有一种更简单的解决方案。将侦听器放入 listview 的适配器并在 getview 中检查 if(position== totalItemCount of items in array to be displayed),如果是,我从适配器调用我的侦听器并在侦听器中运行您的操作,无论它是什么

            【讨论】:

            • 这不考虑虚拟化。在最后一个项目真正可见之前,你会认为你已经走到了尽头。
            【解决方案9】:

            我在尝试从 OnScrollListener 中判断 List 是否位于底部时遇到了很多问题,我找到的最佳解决方案是检查 如果我当前显示最后一行,则来自适配器的 getView 方法:

            @Override
                public View getView(int position, View convertView, ViewGroup parent) {
                    ActivityView view;
                    if (convertView==null)
                        view = (ActivityView)inflater.inflate(R.layout.activity_item, null);
                    else
                        view = (ActivityView)convertView;
            
                   view.showRow(activitiesList.get(position),position,controller,this);
                    if(position == activitiesList.size() - 1)
                    {
                        Log.i("BOTTOM","reached");
                    }
                    return view;
                }
            

            在那里我可以轻松地创建一个接口来通知我到达底部的片段/活动。

            干杯。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-12-02
              • 1970-01-01
              • 2011-04-27
              • 1970-01-01
              • 1970-01-01
              • 2012-10-30
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多