Дізнайтеся, чи ListView прокручується донизу?


105

Чи можу я дізнатися, чи мій список ListView прокручується донизу? Я маю на увазі, що останній елемент повністю видно.

Відповіді:


171

Відредаговано :

Оскільки я досліджував цю тему в одній із моїх заявок, я можу написати розширену відповідь для майбутніх читачів цього питання.

Реалізуйте OnScrollListener, встановіть свої ListView, onScrollListenerі тоді ви повинні мати можливість правильно поводитися з речами.

Наприклад:

private int preLast;
// Initialization stuff.
yourListView.setOnScrollListener(this);

// ... ... ...

@Override
public void onScroll(AbsListView lw, final int firstVisibleItem,
        final int visibleItemCount, final int totalItemCount)
{

    switch(lw.getId()) 
    {
        case R.id.your_list_id:     

            // Make your calculation stuff here. You have all your
            // needed info from the parameters of this function.

            // Sample calculation to determine if the last 
            // item is fully visible.
            final int lastItem = firstVisibleItem + visibleItemCount;

            if(lastItem == totalItemCount)
            {
                if(preLast!=lastItem)
                {
                    //to avoid multiple calls for last item
                    Log.d("Last", "Last");
                    preLast = lastItem;
                }
            }
    }
}

26
Це не визначить, чи останній елемент повністю видно.
фхучо

1
Якщо у вашому перегляді списку є заголовки та колонтитули, ви також повинні це враховувати.
Мартін Марконніні

5
@Wroclai Це не визначить, чи останній елемент повністю видно.
Gaurav Arora

Я шукав подібний РОБОЧИЙ код .. Це спрацювало !! Дуже дякую!!
Сурай Дюбі

Я не хочу починати нове запитання, але що робити, якщо listviewє stackFromBottom? Я намагався, if (0 == firstVisibleItem){//listviewtop}але мені дзвонять неодноразово.
shadyinside

68

Пізня відповідь, але якщо ви просто хочете перевірити, чи ваш ListView прокручується до кінця чи ні, не створюючи слухача подій, ви можете використовувати цей if-оператор:

if (yourListView.getLastVisiblePosition() == yourListView.getAdapter().getCount() -1 &&
    yourListView.getChildAt(yourListView.getChildCount() - 1).getBottom() <= yourListView.getHeight())
{
    //It is scrolled all the way down here

}

Спочатку він перевіряє, чи є остання можлива позиція. Потім він перевіряє, чи нижня частина останньої кнопки вирівнюється до нижньої частини ListView. Ви можете зробити щось подібне, щоб дізнатись, чи все це вгорі:

if (yourListView.getFirstVisiblePosition() == 0 &&
    yourListView.getChildAt(0).getTop() >= 0)
{
    //It is scrolled all the way up here

}

4
Дякую. Просто потрібно змінити ... getChildAt (yourListView.getCount () -1) ... на ... getChildAt (yourListView.getChildCount () -1) ...
OferR

@OferR Не впевнений, але я думаю, що getChildCount()повертає погляди в групу перегляду, яка при переробці перегляду не збігається з кількістю елементів у адаптері. Однак, оскільки ListView походить від AdapterView, ви можете користуватися getCount()безпосередньо на ListView.
Вільям Т. Маллард

@ Вільям Т. Маллард Ваше перше речення правильне, і саме цього ми хочемо: Останній перегляд у групі. Це для того, щоб перевірити, чи воно повністю видно (Розглянемо ListView з 20 рядками, але відображаються лише останні 8. Ми хочемо отримати 8-й вид у ViewGroup, а не 20-го, якого не існує)
OferR

зауважте, що це рішення не працюватиме, якщо ви швидко оновлюєте подання списку.
білка

19

Як я це зробив:

listView.setOnScrollListener(new AbsListView.OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE 
            && (listView.getLastVisiblePosition() - listView.getHeaderViewsCount() -
            listView.getFooterViewsCount()) >= (adapter.getCount() - 1)) {

        // Now your listview has hit the bottom
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    }
});

1
Це приголомшливо! Спасибі
шаріший іслам


6
public void onScrollStateChanged(AbsListView view, int scrollState)        
{
    if (!view.canScrollList(View.SCROLL_AXIS_VERTICAL) && scrollState == SCROLL_STATE_IDLE)    
    {
        //When List reaches bottom and the list isn't moving (is idle)
    }
}

Це працювало для мене.


1
Метод view.canScrollList є API 19+ лише на жаль
ozmank

Працює як шарм
pavaniiitn

4

Це може бути

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // TODO Auto-generated method stub

                if (scrollState == 2)
                    flag = true;
                Log.i("Scroll State", "" + scrollState);
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
                // TODO Auto-generated method stub
                if ((visibleItemCount == (totalItemCount - firstVisibleItem))
                        && flag) {
                    flag = false;



                    Log.i("Scroll", "Ended");
                }
            }

3

Було досить болісно працювати з прокруткою, виявляти, коли вона закінчена, і вона справді знаходиться внизу списку (не внизу видимого екрана), і запускає мою службу лише один раз, щоб отримати дані з Інтернету. Однак зараз це добре працює. Кодекс наступний на користь того, хто стикається з тією ж ситуацією.

ПРИМІТКА. Мені довелося перенести свій пов'язаний з адаптером код у onViewCreate замість onCreate та виявити прокручування в основному так:

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (getListView().getLastVisiblePosition() == (adapter.getCount() - 1))
        if (RideListSimpleCursorAdapter.REACHED_THE_END) {
            Log.v(TAG, "Loading more data");
            RideListSimpleCursorAdapter.REACHED_THE_END = false;
            Intent intent = new Intent(getActivity().getApplicationContext(), FindRideService.class);
            getActivity().getApplicationContext().startService(intent);
        }
}

Тут RideListSimpleCursorAdapter.REACHED_THE_END - додаткова змінна в моєму SimpleCustomAdapter, яка встановлена ​​так:

if (position == getCount() - 1) {
      REACHED_THE_END = true;
    } else {
      REACHED_THE_END = false;
    }

Тільки тоді, коли обидві ці умови відповідають, це означає, що я справді внизу списку, і що моя служба працюватиме лише один раз. Якщо я не вловлю REACHED_THE_END, навіть прокручування назад запускає послугу знову, доки видно останній елемент.


Я позбувся іншого, тому що це спричиняло мені проблеми, але в цілому хороша відповідь
Майк Багліо-молодший

3

canScrollVertically(int direction)працює для всіх переглядів і, здається, виконує те, що ви просили, з меншим кодом, ніж більшість інших відповідей. Підключіть позитивну кількість, і якщо результат помилковий, ви знаходитесь внизу.

тобто:

if (!yourView.canScrollVertically(1)) { //you've reached bottom }


2

Щоб трохи розширити один з вищезазначених відповідей, це те, що я повинен був зробити, щоб повністю працювати. Здається, існує близько 6dp вбудованого прокладки всередині ListViews, і onScroll () викликався, коли список був порожнім. Це обробляє обидва ці речі. Можливо, це може бути трохи оптимізовано, але написано більше для наочності.

Побічна примітка: я спробував кілька різних методів перетворення dp у пікселі, і цей dp2px () був найкращим.

myListView.setOnScrollListener(new OnScrollListener() {
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (visibleItemCount > 0) {
            boolean atStart = true;
            boolean atEnd = true;

            View firstView = view.getChildAt(0);
            if ((firstVisibleItem > 0) ||
                    ((firstVisibleItem == 0) && (firstView.getTop() < (dp2px(6) - 1)))) {
                // not at start
                atStart = false;
            }

            int lastVisibleItem = firstVisibleItem + visibleItemCount;
            View lastView = view.getChildAt(visibleItemCount - 1);
            if ((lastVisibleItem < totalItemCount) ||
                    ((lastVisibleItem == totalItemCount) &&
                            ((view.getHeight() - (dp2px(6) - 1)) < lastView.getBottom()))
                    ) {
                        // not at end
                    atEnd = false;
                }

            // now use atStart and atEnd to do whatever you need to do
            // ...
        }
    }
    public void onScrollStateChanged(AbsListView view, int scrollState) {
    }
});

private int dp2px(int dp) {
    return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

2

Я поки не можу коментувати, тому що у мене недостатньо репутації, але у відповіді @Ali Imran та @Wroclai я думаю, що чогось не вистачає. З цим фрагментом коду, коли ви оновите preLast, він більше ніколи не виконає Журнал. У своїй конкретній проблемі я хочу виконати якусь операцію щоразу, коли прокручую донизу, але після того, як preLast буде оновлений до LastItem, ця операція більше ніколи не виконується.

private int preLast;
// Initialization stuff.
yourListView.setOnScrollListener(this);

// ... ... ...

@Override
public void onScroll(AbsListView lw, final int firstVisibleItem,
                 final int visibleItemCount, final int totalItemCount) {

switch(lw.getId()) {
    case android.R.id.list:     

        // Make your calculation stuff here. You have all your
        // needed info from the parameters of this function.

        // Sample calculation to determine if the last 
        // item is fully visible.
         final int lastItem = firstVisibleItem + visibleItemCount;
       if(lastItem == totalItemCount) {
          if(preLast!=lastItem){ //to avoid multiple calls for last item
            Log.d("Last", "Last");
            preLast = lastItem;
          }
       } else {
            preLast = lastItem;
}

}

Завдяки цьому "else" ви тепер можете виконувати свій код (Журнал, в даному випадку) кожен раз, коли ви прокручуєте донизу ще раз.


2
public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {
        int lastindex = view.getLastVisiblePosition() + 1;

        if (lastindex == totalItemCount) { //showing last row
            if ((view.getChildAt(visibleItemCount - 1)).getTop() == view.getHeight()) {
                //Last row fully visible
            }
        }
    }

1

Щоб ваш список зателефонував, коли список досягає останнього, і якщо станеться помилка, це не повторить виклик перегляду endoflist . Цей код також допоможе цьому сценарію.

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount) {
    final int lastPosition = firstVisibleItem + visibleItemCount;
    if (lastPosition == totalItemCount) {
        if (previousLastPosition != lastPosition) { 

            //APPLY YOUR LOGIC HERE
        }
        previousLastPosition = lastPosition;
    }
    else if(lastPosition < previousLastPosition - LIST_UP_THRESHOLD_VALUE){
        resetLastIndex();
    }
}

public void resetLastIndex(){
    previousLastPosition = 0;
}

де LIST_UP_THRESHOLD_VALUE може бути будь-яким цілим значенням (я використав 5), де ваш список прокручується вгору, і, повертаючись до кінця, це знову викликатиме перегляд списку.


1

Я знайшов дуже приємний спосіб автоматичного завантаження наступного набору сторінок таким чином, що не вимагає вашого власного ScrollView(як, наприклад, вимагає прийнята відповідь).

На ParseQueryAdapter існує метод, який називається, getNextPageViewщо дозволяє вам надати власний власний вид, який з’являється в кінці списку, коли буде завантажено більше даних, і він запуститься лише тоді, коли ви досягнете кінця поточного набору сторінок (за замовчуванням це перегляд "завантажити більше .."). Цей метод викликається лише тоді, коли для завантаження більше даних, тому це чудове місце для виклику. loadNextPage(); Таким чином адаптер виконує всю важку роботу для визначення того, коли потрібно завантажувати нові дані, і він взагалі не буде викликаний, якщо у вас є досягли кінця набору даних.

public class YourAdapter extends ParseQueryAdapter<ParseObject> {

..

@Override
public View getNextPageView(View v, ViewGroup parent) {
   loadNextPage();
   return super.getNextPageView(v, parent);
  }

}

Тоді всередині вашої діяльності / фрагменту вам просто потрібно встановити адаптер, і нові дані будуть автоматично оновлюватися для вас, як магія.

adapter = new YourAdapter(getActivity().getApplicationContext());
adapter.setObjectsPerPage(15);
adapter.setPaginationEnabled(true);
yourList.setAdapter(adapter);

1

Щоб визначити, чи останній елемент повністю видно , ви можете просто додати обчислення внизу останнього видимого елемента, на lastItem.getBottom().

yourListView.setOnScrollListener(this);   

@Override
public void onScroll(AbsListView view, final int firstVisibleItem,
                 final int visibleItemCount, final int totalItemCount) {

    int vH = view.getHeight();
    int topPos = view.getChildAt(0).getTop();
    int bottomPos = view.getChildAt(visibleItemCount - 1).getBottom();

    switch(view.getId()) {
        case R.id.your_list_view_id:
            if(firstVisibleItem == 0 && topPos == 0) {
                //TODO things to do when the list view scroll to the top
            }

            if(firstVisibleItem + visibleItemCount == totalItemCount 
                && vH >= bottomPos) {
                //TODO things to do when the list view scroll to the bottom
            }
            break;
    }
}

1

Я пішов:

@Override
public void onScroll(AbsListView listView, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
    if(totalItemCount - 1 == favoriteContactsListView.getLastVisiblePosition())
    {
        int pos = totalItemCount - favoriteContactsListView.getFirstVisiblePosition() - 1;
        View last_item = favoriteContactsListView.getChildAt(pos);

        //do stuff
    }
}

1

У методі getView()( BaseAdapterкласу -похідних) можна перевірити, чи положення поточного представлення дорівнює переліку елементів у Adapter. Якщо це так, то це означає, що ми дійшли до кінця / нижньої частини списку:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // ...

    // detect if the adapter (of the ListView/GridView) has reached the end
    if (position == getCount() - 1) {
        // ... end of list reached
    }
}

0

Я знаходжу кращий спосіб виявити прокрутку listview в кінці внизу, спочатку виявити кінець черепа за допомогою цієї
реалізації OnScrollListener, щоб виявити кінець прокрутки в ListView

 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! ***/
    }
}

нарешті поєднайте відповідь Мартійна

OnScrollListener onScrollListener_listview = new OnScrollListener() {       

        private int currentScrollState;
        private int currentVisibleItemCount;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            // TODO Auto-generated method stub

            this.currentScrollState = scrollState;
            this.isScrollCompleted();
        }

        @Override
        public void onScroll(AbsListView lw, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
            // TODO Auto-generated method stub
            this.currentVisibleItemCount = visibleItemCount;

        }

        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! ***/

                if (listview.getLastVisiblePosition() == listview.getAdapter().getCount() - 1
                        && listview.getChildAt(listview.getChildCount() - 1).getBottom() <= listview.getHeight()) {
                    // It is scrolled all the way down here
                    Log.d("henrytest", "hit bottom");
                }


            }
        }

    };

0

Велика подяка плакатам у stackoverflow! Я поєднав деякі ідеї та створив слухача класів для діяльності та фрагментів (тому цей код є більш багаторазовим, що робить код швидшим для запису та набагато чистішим).

Все, що вам потрібно зробити, коли ви отримали мій клас, - це реалізувати інтерфейс (і, звичайно, створити для нього метод), який знаходиться в оголошеному в моєму класі, і створити об'єкт цього класу, передаючи аргументи.

/**
* Listener for getting call when ListView gets scrolled to bottom
*/
public class ListViewScrolledToBottomListener implements AbsListView.OnScrollListener {

ListViewScrolledToBottomCallback scrolledToBottomCallback;

private int currentFirstVisibleItem;
private int currentVisibleItemCount;
private int totalItemCount;
private int currentScrollState;

public interface ListViewScrolledToBottomCallback {
    public void onScrolledToBottom();
}

public ListViewScrolledToBottomListener(Fragment fragment, ListView listView) {
    try {
        scrolledToBottomCallback = (ListViewScrolledToBottomCallback) fragment;
        listView.setOnScrollListener(this);
    } catch (ClassCastException e) {
        throw new ClassCastException(fragment.toString()
                + " must implement ListViewScrolledToBottomCallback");
    }
}

public ListViewScrolledToBottomListener(Activity activity, ListView listView) {
    try {
        scrolledToBottomCallback = (ListViewScrolledToBottomCallback) activity;
        listView.setOnScrollListener(this);
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement ListViewScrolledToBottomCallback");
    }
}

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

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
    this.currentScrollState = scrollState;
    if (isScrollCompleted()) {
        if (isScrolledToBottom()) {
            scrolledToBottomCallback.onScrolledToBottom();
        }
    }
}

private boolean isScrollCompleted() {
    if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) {
        return true;
    } else {
        return false;
    }
}

private boolean isScrolledToBottom() {
    System.out.println("First:" + currentFirstVisibleItem);
    System.out.println("Current count:" + currentVisibleItemCount);
    System.out.println("Total count:" + totalItemCount);
    int lastItem = currentFirstVisibleItem + currentVisibleItemCount;
    if (lastItem == totalItemCount) {
        return true;
    } else {
        return false;
    }
}
}

0

Потрібно додати порожній ресурс нижнього колонтитулу xml до свого спискуПереглядати та виявляти, чи бачиться цей колонтитул.

    private View listViewFooter;
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_newsfeed, container, false);

        listView = (CardListView) rootView.findViewById(R.id.newsfeed_list);
        footer = inflater.inflate(R.layout.newsfeed_listview_footer, null);
        listView.addFooterView(footer);

        return rootView;
    }

Потім у своєму спискуView прослуховувач прокрутки ви це зробите

@
Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  if (firstVisibleItem == 0) {
    mSwipyRefreshLayout.setDirection(SwipyRefreshLayoutDirection.TOP);
    mSwipyRefreshLayout.setEnabled(true);
  } else if (firstVisibleItem + visibleItemCount == totalItemCount) //If last row is visible. In this case, the last row is the footer.
  {
    if (footer != null) //footer is a variable referencing the footer view of the ListView. You need to initialize this onCreate
    {
      if (listView.getHeight() == footer.getBottom()) { //Check if the whole footer is visible.
        mSwipyRefreshLayout.setDirection(SwipyRefreshLayoutDirection.BOTTOM);
        mSwipyRefreshLayout.setEnabled(true);
      }
    }
  } else
    mSwipyRefreshLayout.setEnabled(false);
}


0

Якщо ви встановите тег для подання останнього елемента перегляду списку, пізніше ви можете отримати його за допомогою тегу, якщо подання є нульовим, це тому, що представлення більше не завантажується. Подобається це:

private class YourAdapter extends CursorAdapter {
    public void bindView(View view, Context context, Cursor cursor) {

         if (cursor.isLast()) {
            viewInYourList.setTag("last");
         }
         else{
            viewInYourList.setTag("notLast");
         }

    }
}

то якщо вам потрібно знати, чи завантажений останній елемент

View last = yourListView.findViewWithTag("last");
if (last != null) {               
   // do what you want to do
}

0

Janwilx72 вірно, але це min sdk - 21, тому я створю цей метод:

private boolean canScrollList(@ScrollOrientation int direction, AbsListView listView) {
    final int childCount = listView.getChildCount();
    if (childCount == 0) {
        return false;
    }

    final int firstPos = listView.getFirstVisiblePosition();
    final int paddingBottom = listView.getListPaddingBottom();
    final int paddingTop = listView.getListPaddingTop();
    if (direction > 0) {
        final int lastBottom = listView.getChildAt(childCount - 1).getBottom();
        final int lastPos    = firstPos + childCount;
        return lastPos < listView.getChildCount() || lastBottom > listView.getHeight() - paddingBottom;
    } else {
        final int firstTop = listView.getChildAt(0).getTop();
        return firstPos > 0 || firstTop < paddingTop;
    }
}

для ScrollOrientation:

protected static final int SCROLL_UP = -1;
protected static final int SCROLL_DOWN = 1;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SCROLL_UP, SCROLL_DOWN})
protected @interface Scroll_Orientation{}

Можливо, пізно, просто для пізніх комерсантів。


0

Якщо ви використовуєте спеціальний адаптер з переглядом списку (більшість людей це робить!), Тут надається прекрасне рішення!

https://stackoverflow.com/a/55350409/1845404

Метод getView адаптера визначає, коли список прокручено до останнього елемента. Він також додає корекцію для рідкісних випадків, коли викликається деяка попередня позиція, навіть після того, як адаптер вже надав останній вигляд.


0

Я це зробив і працює для мене:

private void YourListView_Scrolled(object sender, ScrolledEventArgs e)
        {
                double itemheight = YourListView.RowHeight;
                double fullHeight = YourListView.Count * itemheight;
                double ViewHeight = YourListView.Height;

                if ((fullHeight - e.ScrollY) < ViewHeight )
                {
                    DisplayAlert("Reached", "We got to the end", "OK");
                }
}

-5

Це прокрутить список вниз до останнього запису.

ListView listView = new ListView(this);
listView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
listView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
listView.setStackFromBottom(true);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.