Внедрение onScrollListener для обнаружения конца прокрутки в ListView
У меня есть ListView
отображение некоторых элементов. Я хотел бы выполнить некоторую операцию над элементами, которые в настоящее время отображаются в видимой части ListView
, в зависимости от того, как прокручивается ListView
; таким образом, я решил реализовать OnScrollListener
ListView
.
В соответствии с ссылкой на api для Android, метод onScroll будет вызван после завершения прокрутки. Мне кажется, что мне нужно, как только прокрутка завершилась, я выполняю свои действия в ListView
(метод onScroll возвращает индекс первого отображаемого элемента и количество отображаемых элементов).
Но после реализации я вижу из LogCat
, что метод onScroll не только запускается после завершения прокрутки, но и запускается для каждого нового элемента, который входит в представление отображения, от начала до конца прокрутки, Это не то поведение, которое я ожидаю, и мне не нужно. Другой метод прослушивателя (onScrollStateChanged) вместо этого не предоставляет информацию о элементах, отображаемых в настоящее время в ListView
.
Итак, кто-нибудь знает, как использовать эту пару методов для обнаружения окончания прокрутки и получения информации о отображаемых элементах? Несоосность между ссылкой api и фактическим поведением метода немного смутила меня.
Спасибо заранее.
P.S.: Я видел некоторые похожие темы, но ничто не помогает мне понять, как все это работает..!
Ответы
Ответ 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 been a scroll which has completed ***/
/*** do the work! ***/
}
}
Практически каждый раз, когда прокручивается ListView, я сохраняю данные о первом видимом элементе и счету видимого элемента (метод onScroll); когда состояние прокрутки изменяется (onScrollStateChanged), я сохраняю состояние, и я вызываю другой метод, который фактически понимает, был ли прокруток и завершен ли он. Таким образом, у меня также есть данные о видимых элементах, которые мне нужны.
Может быть, не чистый, но работает!
Привет
Ответ 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 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
к вашим конструкторам. Я думаю, эта реализация хороша, потому что она не будет сразу запускать "событие", она будет немного ждать, пока она полностью не остановит прокрутку.
Ответ 5
Попробуйте этот...
@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
}
}
Ответ 6
Просто более полезное представление e-cal 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!
}
});
Ответ 7
Анализ предыдущих ответов, нашел свое:
В 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
}
}
Кажется, он должен: обнаружить конец списка без тонны кода и модификаций адаптера.
Ответ 8
Есть еще одно простое решение. Поместите слушателя в Adapter of listview и в checkview проверьте, если (position == totalItemCount элементов в массиве будет отображаться), если да, я вызываю своего слушателя из адаптера и в Listener. Запустите свою работу независимо от того, что это будет
Ответ 9
У меня возникло много проблем при попытке выяснить, находится ли Список внизу от 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;
}
там я могу легко создать интерфейс для уведомления Фрагмента/Действия, который я достиг нижней.
приветствий.