42

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

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

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

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

4

9 回答 9

54

最后,我找到了一个不太优雅但对我有用的解决方案;发现滚动的每一步都调用 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),我保存状态并调用另一个方法,该方法实际上了解是否存在滚动以及是否已完成。通过这种方式,我也有关于我需要的可见项目的数据。也许不干净但有效!

问候

于 2011-06-17T10:46:33.297 回答
13
@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
  }
}
于 2013-09-13T07:21:01.590 回答
9
@Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {

            if((firstVisibleItem + visibleItemCount) ==  totalItemCount)
            {
                Log.i("Info", "Scroll Bottom" );
            }
        }});
于 2014-06-17T01:25:07.753 回答
6

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

我认为我能做到的最好方法是扩展 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在构造函数中绑定 。我认为这个实现很好,因为它不会立即触发“事件”,它会等待一段时间,直到它实际上完全停止滚动。

于 2011-06-15T13:55:25.643 回答
4

分析之前的答案,发现了自己的:

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

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

于 2015-12-25T15:57:32.847 回答
3

试试这个...

@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
    }
}
于 2015-04-21T06:49:28.993 回答
3

只是更有用的e-cal 答案的表示

监听器实现:

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!
         }
    });
于 2015-10-02T09:12:12.593 回答
0

还有一种更简单的解决方案。将侦听器放入 listview 的适配器并在 getview 检查是否(位置==要显示的数组中的项目的总项目数),如果是,我从适配器调用我的侦听器并在侦听器中运行您的操作,无论它是什么

于 2013-04-18T08:57:01.913 回答
0

在尝试确定 List 是否位于 OnScrollListener 的底部时,我遇到了很多问题,我发现的最佳解决方案是从适配器的 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;
    }

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

干杯。

于 2015-05-06T03:04:19.537 回答