Бесконечный список Android

232

Как я могу создать список, когда, когда вы достигнете конца списка, я получу уведомление, чтобы я мог загрузить больше элементов?

Исаак Уоллер
источник
9
Я рекомендую EndlessAdapter от CommonWare: github.com/commonsguy/cwac-endless . Я нашел это из этого ответа на другой вопрос: stackoverflow.com/questions/4667064/… .
Тайлер Кольер
Мне нужна та же вещь ... может ли какая-нибудь помощь? .. stackoverflow.com/questions/28122304/…

Ответы:

269

Одним из решений является реализация OnScrollListenerи внесение изменений (например, добавление элементов и т. Д.) В ListAdapterудобное состояние onScrollметода.

Ниже ListActivityприведен список целых чисел, начиная с 40, добавление элементов при прокрутке пользователем до конца списка.

public class Test extends ListActivity implements OnScrollListener {

    Aleph0 adapter = new Aleph0();

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setListAdapter(adapter); 
        getListView().setOnScrollListener(this);
    }

    public void onScroll(AbsListView view,
        int firstVisible, int visibleCount, int totalCount) {

        boolean loadMore = /* maybe add a padding */
            firstVisible + visibleCount >= totalCount;

        if(loadMore) {
            adapter.count += visibleCount; // or any other amount
            adapter.notifyDataSetChanged();
        }
    }

    public void onScrollStateChanged(AbsListView v, int s) { }    

    class Aleph0 extends BaseAdapter {
        int count = 40; /* starting amount */

        public int getCount() { return count; }
        public Object getItem(int pos) { return pos; }
        public long getItemId(int pos) { return pos; }

        public View getView(int pos, View v, ViewGroup p) {
                TextView view = new TextView(Test.this);
                view.setText("entry " + pos);
                return view;
        }
    }
}

Очевидно, что вы должны использовать отдельные потоки для длительных действий (например, загрузки веб-данных) и, возможно, захотите указать прогресс в последнем элементе списка (как это делают приложения market или gmail).

Йозеф Пфлегер
источник
3
да, не приведет ли это к увеличению размера адаптера, если вы перестанете прокручивать середину экрана? он должен загружать только следующие 10, когда фактически достигнет дна списка.
Матиас
9
Я заметил много примеров, подобных этому, с помощью BaseAdapter. Хотел бы упомянуть, что если вы используете базу данных, попробуйте использовать SimpleCursorAdapter. Когда вам нужно добавить в список, обновить базу данных, получить новый курсор и использовать SimpleCursorAdapter # changeCursor вместе с notifyDataSetChanged. Подклассы не требуются, и он работает лучше, чем BaseAdapter (опять же, только если вы используете базу данных).
Брек
1
После вызова notifyDataSetChanged () он станет первым в списке, как я могу предотвратить это?
ничья
2
Примечание. Я использовал этот подход, но мне нужно было добавить проверку, когда visibleCount равен 0. Для каждого списка я получаю один обратный вызов onScroll, где firstVisible, visibleCount и totalCount равны 0, что технически соответствует условию, но не когда я хочу загрузить больше.
Эрик Милл
5
Другая проблема с этим кодом заключается в том, что условие firstVisible + visibleCount >= totalCountвыполняется несколько раз при нескольких вызовах слушателя. Если функция load-more является веб-запросом (скорее всего, так и будет), добавьте еще одну проверку, выполняется ли запрос или нет. С другой стороны, проверьте, не равен ли totalCount предыдущему общему количеству, потому что нам не нужно несколько запросов для одного и того же количества элементов в списке.
Субин Себастьян
94

Просто хотел предложить решение, которое я использовал для своего приложения.

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

  1. Пусть ваш ListFragmentили ListActivityреализоватьOnScrollListener
  2. Добавьте следующие методы к этому классу:

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        //leave this empty
    }
    
    @Override
    public void onScrollStateChanged(AbsListView listView, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {
            if (listView.getLastVisiblePosition() >= listView.getCount() - 1 - threshold) {
                currentPage++;
                //load more list items:
                loadElements(currentPage);
            }
        }
    }

    где currentPage- страница вашего источника данных, которая должна быть добавлена ​​в ваш список, и thresholdколичество элементов списка (отсчитываемых с конца), которые, если они видны, должны инициировать процесс загрузки. Если вы установите thresholdна 0, например, пользователь должен прокрутить до самого конца списка, чтобы загрузить больше деталей.

  3. (необязательно) Как видите, «проверка загрузки» вызывается только тогда, когда пользователь прекращает прокрутку. Чтобы улучшить удобство использования, вы можете надуть и добавить индикатор загрузки в конец списка через listView.addFooterView(yourFooterView). Один пример для такого представления нижнего колонтитула:

    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/footer_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" >
    
        <ProgressBar
            android:id="@+id/progressBar1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/progressBar1"
            android:padding="5dp"
            android:text="@string/loading_text" />
    
    </RelativeLayout>
  4. (необязательно) Наконец, удалите этот индикатор загрузки, позвонив, listView.removeFooterView(yourFooterView)если больше нет элементов или страниц.

saschoar
источник
Я пробовал это, но это не работает для ListFragment. Как это можно использовать в ListFragment.
Зеешан
Ну, я использую его ListFragmentтакже без проблем - просто следуйте каждому из шагов, описанных выше, и все будет в порядке;) Или вы могли бы предоставить какие-либо сообщения об ошибках?
saschoar
То, что вы, возможно, пропустили, объясняется в этом SO-ответе: stackoverflow.com/a/6357957/1478093 -getListView().setOnScrollListener(this)
TBieniek
7
примечание: добавление и удаление нижнего колонтитула при просмотре списка проблематично (нижний колонтитул не отображается, если setAdapter вызывается перед добавлением нижнего колонтитула), в итоге я изменил видимость представления нижнего колонтитула напрямую, не удаляя его.
dvd
Что должно быть добавлено в loadElements (currentPage) ??? Я имею в виду, я называю мой AsyncTask или поток?
android_developer
20

Вы можете определить конец списка с помощью onScrollListener , рабочий код представлен ниже:

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (view.getAdapter() != null && ((firstVisibleItem + visibleItemCount) >= totalItemCount) && totalItemCount != mPrevTotalItemCount) {
        Log.v(TAG, "onListEnd, extending list");
        mPrevTotalItemCount = totalItemCount;
        mAdapter.addMoreData();
    }
}

Другой способ сделать это (внутри адаптера) заключается в следующем:

    public View getView(int pos, View v, ViewGroup p) {
            if(pos==getCount()-1){
                addMoreData(); //should be asynctask or thread
            }
            return view;
    }

Помните, что этот метод будет вызываться много раз, поэтому вам нужно добавить еще одно условие, чтобы заблокировать несколько вызовов addMoreData().

Когда вы добавляете все элементы в список, пожалуйста, вызовите notifyDataSetChanged () внутри вашего адаптера, чтобы обновить представление (его следует запускать в потоке пользовательского интерфейса - runOnUiThread )

Дариуш Бачински
источник
7
Это плохая практика - делать что-то иное, чем возвращать представление getView. Например, вам не гарантировано, что posпросматривает пользователь. Это может быть просто измерение ListView.
Томас Ахл
Я хочу загрузить больше элементов после того, как 10 элементов прокручиваются. но я замечаю, что загрузка начинается до того, как 10-й элемент становится видимым, значит на 9-м элементе. Так как это остановить?
Атул Бхардвай
4

В Ognyan Bankov GitHubя нашел простое и работающее решение!

Это делает использование Volley HTTP libraryсетей Android-приложений проще, а главное, быстрее. Залп доступен через открытый репозиторий AOSP.

Данный код демонстрирует:

  1. ListView, который заполняется постраничными запросами HTTP.
  2. Использование NetworkImageView.
  3. «Бесконечная» нумерация страниц ListView с упреждающим чтением.

Для будущей консистенции я раздвоил репо Банкова .

oikonomopo
источник
2

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

Вы можете увидеть классы здесь:

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java - помощник, позволяющий использовать функции без расширения из SimpleListViewWithLoadingIndicator.

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java - прослушиватель, который начинает загрузку данных, когда пользователь собирается достичь дна ListView.

https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java - The EndlessListView. Вы можете использовать этот класс напрямую или расширять его.

Фернандо Камарго
источник
1

Может быть, немного поздно, но следующее решение оказалось очень полезным в моем случае. Таким образом, все, что вам нужно сделать, это добавить в свой ListView Footerи создать для него addOnLayoutChangeListener.

http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)

Например:

ListView listView1 = (ListView) v.findViewById(R.id.dialogsList); // Your listView
View loadMoreView = getActivity().getLayoutInflater().inflate(R.layout.list_load_more, null); // Getting your layout of FooterView, which will always be at the bottom of your listview. E.g. you may place on it the ProgressBar or leave it empty-layout.
listView1.addFooterView(loadMoreView); // Adding your View to your listview 

...

loadMoreView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
         Log.d("Hey!", "Your list has reached bottom");
    }
});

Это событие срабатывает один раз, когда нижний колонтитул становится видимым и работает как шарм.

tehcpu
источник
0

Ключ этой проблемы - обнаружить событие load-more, запустить асинхронный запрос данных и затем обновить список. Также необходим адаптер с индикатором загрузки и другими декораторами. На самом деле, проблема очень сложна в некоторых угловых случаях. Просто OnScrollListenerреализации недостаточно, потому что иногда элементы не заполняют экран.

Я написал персональный пакет, который поддерживает бесконечный список RecyclerView, а также предоставляет реализацию асинхронного загрузчика, AutoPagerFragmentкоторая позволяет очень легко получать данные из многостраничного источника. Он может загрузить любую страницу в RecyclerViewпользовательское событие, а не только следующую страницу.

Вот адрес: https://github.com/SphiaTower/AutoPagerRecyclerManager

ProtossShuttle
источник
Ссылка не работает.
DSlomer64
0

Лучшее решение, которое я когда-либо видел, - это библиотека FastAdapter для представлений переработчика. Это имеет EndlessRecyclerOnScrollListener.

Вот пример использования: EndlessScrollListActivity

Как только я использовал его для бесконечной прокрутки списка, я понял, что настройка очень надежна. Я определенно рекомендую это.

Шубхам Чаудхари
источник
0

Я работал над другим решением, очень похожим на это, но я использую footerViewзначок footerView, чтобы дать возможность пользователю загружать больше элементов, щелкнув по кнопке « Я», использую «меню», которое показано над ListViewи в нижней части окна. В родительском представлении это «меню» скрывает нижнюю часть ListView, поэтому при listViewпрокрутке меню исчезает, а когда состояние прокрутки бездействует, меню появляется снова, но когда пользователь прокручивает до конца listView, я «прошу» знать footerView, отображается ли в этом случае, меню не появляется, и пользователь может видеть, footerViewчтобы загрузить больше контента. Вот код:

С уважением.

listView.setOnScrollListener(new OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        if(scrollState == SCROLL_STATE_IDLE) {
            if(footerView.isShown()) {
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            } else {
                bottomView.setVisibility(LinearLayout.VISIBLE);
            } else {
                bottomView.setVisibility(LinearLayout.INVISIBLE);
            }
        }
    }

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

    }
});
pabloverd
источник
0

Я знаю, что это старый вопрос, и мир Android в основном перешел на RecyclerViews, но для всех, кому это интересно, эта библиотека может оказаться очень интересной.

Он использует BaseAdapter, используемый с ListView, чтобы определить, когда список был прокручен до последнего элемента или когда он прокручивается от последнего элемента.

Он поставляется с примером проекта (всего 100 строк кода Activity), который можно использовать, чтобы быстро понять, как он работает.

Простое использование:

class Boy{

private String name;
private double height;
private int age;
//Other code

}

Адаптер для хранения объектов Boy будет выглядеть так:


public class BoysAdapter extends EndlessAdapter<Boy>{




        ViewHolder holder = null;


        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent
                    .getContext());

            holder = new ViewHolder();

            convertView = inflater.inflate(
                    R.layout.list_cell, parent, false);


            holder.nameView = convertView.findViewById(R.id.cell);

            // minimize the default image.
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Boy boy = getItem(position);

        try {
            holder.nameView.setText(boy.getName());

            ///Other data rendering codes.

        } catch (Exception e) {
            e.printStackTrace();
        }

        return super.getView(position,convertView,parent);

}

Обратите внимание, как метод getView BoysAdapter возвращает вызов метода суперкласса EndlessAdapter getView. Это на 100% важно.

Теперь, чтобы создать адаптер, выполните:

   adapter = new ModelAdapter() {
            @Override
            public void onScrollToBottom(int bottomIndex, boolean moreItemsCouldBeAvailable) {

                if (moreItemsCouldBeAvailable) { 
                    makeYourServerCallForMoreItems();
                } else {
                    if (loadMore.getVisibility() != View.VISIBLE) {
                        loadMore.setVisibility(View.VISIBLE);
                    }
                }
            }

            @Override
            public void onScrollAwayFromBottom(int currentIndex) { 
                loadMore.setVisibility(View.GONE);
            }

            @Override
            public void onFinishedLoading(boolean moreItemsReceived) { 
                if (!moreItemsReceived) {
                    loadMore.setVisibility(View.VISIBLE);
                }
            }
        };

loadMoreЭлемент используется тот или иной элемент интерфейса , который может быть нажат , чтобы извлечь больше данных из URL. При размещении, как описано в коде, адаптер точно знает, когда показывать эту кнопку и когда отключать ее. Просто создайте кнопку в своем XML-файле и поместите ее, как показано в коде адаптера выше.

Наслаждаться.

gbenroscience
источник