English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Logic
Today, let's implement a ListView that loads more on pull-up. GitHub link:PulmListView, Welcome to fork && star.
Let's first sort out the logic. If we want to implement a ListView that loads more on pull-up, we need to implement the following functions:
1. A custom ListView that can determine if it is currently at the bottom.
2. A custom FooterView for UI display during the ListView loading more process.
3. Associate FooterView with ListView, including loading timing judgment, and the display and hiding of FooterView.
4. Provide an interface for loading more to facilitate the implementation of the user's actual loading more functionality.
5. Provide a callback method for loading more to add the latest user data and update related status flags and UI display.
For the above 5 functions, we will analyze the corresponding implementation methods one by one.
Function 1(Custom ListView)
We can implement a custom PulmListView by inheriting from ListView.
public class PulmListView extends ListView { public PulmListView(Context context) { this(context, null); } public PulmListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PulmListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // Initialization init(); } }
It's not enough to just implement the three constructors of ListView; we need ListView to be able to determine if it has scrolled to the last element.
To determine if we have scrolled to the last element, we can set an OnScrollListener for ListView. The code is as follows:
private void init() { super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Вызов пользователя设置的 OnScrollListener if (mUserOnScrollListener != null) { mUserOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // Вызов пользователя设置的 OnScrollListener if (mUserOnScrollListener != null) { mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } // firstVisibleItem is the position of the first element that can be displayed on the current screen // visibleItemCount is the number of elements that can be displayed on the current screen // totalItemCount - общее количество элементов в ListView int lastVisibleItem = firstVisibleItem + visibleItemCount; if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) { if (mOnPullUpLoadMoreListener != null) { mIsLoading = true; mOnPullUpLoadMoreListener.onPullUpLoadMore(); } } } }); }
Из комментария в коде можно понять, что через (firstVisibleItem + visibleItemCount) можно получить количество элементов, отображенных на экране. Если количество отображенных элементов равно общему количеству элементов ListView, то можно считать, что ListView достиг конца.
Функция 2(Настройка пользовательского FooterView)
Здесь мы можем реализовать довольно простую FooterView, то есть макет для загрузки данных. Например, мы можем показать ProgressBar и строку текста. Конкретный код следующий:
/** * Макет для отображения загрузки данных, может быть настроен. */ public class LoadMoreView extends LinearLayout { public LoadMoreView(Context context) { this(context, null); } public LoadMoreView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public LoadMoreView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { LayoutInflater.from(getContext()).inflate(R.layout.lv_load_more, this); } }
Файл макета:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/id_load_more_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:layout_margin="@dimen/loading_view_margin_layout"> <ProgressBar android:id="@+id/id_loading_progressbar" android:layout_width="@dimen/loading_view_progress_size" android:layout_height="@dimen/loading_view_progress_size" android:indeterminate="true" style="?android:progressBarStyleSmall"/> <TextView android:id="@+id/id_loading_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/page_loading"/> </LinearLayout>
Функция 3(Связь ListView и FooterView)
Во-первых, нам нужно сохранять FooterView в переменной ListView и создавать его экземпляр в конструкторе.
private View mLoadMoreView; private void init() { mLoadMoreView = new LoadMoreView(getContext()); }
Во-вторых, нам нужно контролировать отображение и скрытие FooterView. Рассмотрим时机 для отображения и скрытия FooterView:
• Время отображения: ListView находится в нижней части и есть еще данные для загрузки.
• Время скрытия: ListView завершает загрузку дополнительных данных.
Чтобы определить, нужно ли загружать данные, мы должны определить boolean переменную mIsPageFinished, которая указывает на завершение загрузки данных.
Чтобы обеспечить загрузку данных только один раз в одно и то же время, мы также должны определить boolean переменную mIsLoading, которая указывает на то, что данные уже загружены.
Определено время отображения и скрытия FooterView, а также переменные для управления состоянием, поэтому реализация кода становится более легкой.
Время отображения:
private void init() { mIsLoading = false; // При инициализации не находится в состоянии загрузки mIsPageFinished = false; // По умолчанию при инициализации еще есть данные для загрузки mLoadMoreView = new LoadMoreView(getContext()); // Создание экземпляра FooterView super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Вызов пользователя设置的 OnScrollListener if (mUserOnScrollListener != null) { mUserOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // Вызов пользователя设置的 OnScrollListener if (mUserOnScrollListener != null) { mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } int lastVisibleItem = firstVisibleItem + visibleItemCount; // При нахождении в конце ListView и наличии данных для загрузки, когда нет активного процесса загрузки, выполнить операцию загрузки больше if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) { if (mOnPullUpLoadMoreListener != null) { mIsLoading = true; // Установить состояние загрузки больше в процессе в true showLoadMoreView(); // Показать макет загрузки больше mOnPullUpLoadMoreListener.onPullUpLoadMore(); // Вызов интерфейса обратного вызова загрузки больше } } } }); } private void showLoadMoreView() { // Здесь устанавливается идентификатор корневого макета для загрузки более как id_load_more_layout, чтобы пользователи могли легко настроить макет загрузки более. if (findViewById(R.id.id_load_more_layout) == null) { addFooterView(mLoadMoreView); } }
Время для скрытия:
/** * Callback method after loading more is finished. * * @param isPageFinished Whether pagination is finished * @param newItems Data loaded by pagination * @param isFirstLoad Whether it is the first time to load data (used for configuring the pull-to-refresh framework to avoid page flashing) */ public void onFinishLoading(boolean isPageFinished, List<?> newItems, boolean isFirstLoad) { mIsLoading = false; // Маркер того, что текущая загрузка более не выполняется setIsPageFinished(isPageFinished); // Установить флаг завершения разбиения на страницы и удалить FooterView } private void setIsPageFinished(boolean isPageFinished) { mIsPageFinished = isPageFinished; removeFooterView(mLoadMoreView); }
Функция 4(Интерфейс回调 для реализации загрузки более)
Это довольно просто, мы определяем interface, чтобы упростить вызов пользователя для фактического выполнения загрузки более.
/** * Интерфейс回调 для загрузки более. */ public interface OnPullUpLoadMoreListener { void onPullUpLoadMore(); } private OnPullUpLoadMoreListener mOnPullUpLoadMoreListener; /** * Установить интерфейс回调 для загрузки более. * @param l Интерфейс回调 для загрузки более. */ public void setOnPullUpLoadMoreListener(OnPullUpLoadMoreListener l) { this.mOnPullUpLoadMoreListener = l; }
Функция 5(回调结束时 загрузки более)
Для поддержания集合 данных в PulmListView необходимо создать пользовательский адаптер, в котором использовать List для хранения集合 данных и предоставить методы для добавления и удаления.
Пользовательский адаптер:
/** * Абстрактный адаптер. */ public abstract class PulmBaseAdapter<T> extends BaseAdapter { protected List<T> items; public PulmBaseAdapter() { this.items = new ArrayList<>(); } public PulmBaseAdapter(List<T> items) { this.items = items; } public void addMoreItems(List<T> newItems, boolean isFirstLoad) { if (isFirstLoad) { this.items.clear(); } this.items.addAll(newItems); notifyDataSetChanged(); } public void removeAllItems() { this.items.clear(); notifyDataSetChanged(); } }
Why do we need to add an isFirstLoad variable in the addMoreItems method?
This is because pull-to-load more is usually used in conjunction with pull-to-refresh. During the pull-to-refresh process, the ListView data collection is cleared and then re-added. If the isFirstLoad parameter is not present, the user must update the ListView data collection in two steps when pulling to refresh:
1. Remove all items and then call notifyDataSetChanged.
2. Add more items and then call notifyDataSetChanged.
Two consecutive calls to notifyDataSetChanged at the same time can cause screen flickering, so an isFirstLoad method is submitted here. When it is the first time to load data, all data will be cleared first, then addAll, and finally notify.
With a custom adapter, you can write a callback function for loading more:
/** * Callback method after loading more is finished. * * @param isPageFinished Whether pagination is finished * @param newItems Data loaded by pagination * @param isFirstLoad Whether it is the first time to load data (used for configuring the pull-to-refresh framework to avoid page flashing) */ public void onFinishLoading(boolean isPageFinished, List<?> newItems, boolean isFirstLoad) { mIsLoading = false; setIsPageFinished(isPageFinished); // Добавить обновленные данные if (newItems != null && newItems.size() > 0) { PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter(); adapter.addMoreItems(newItems, isFirstLoad); } }
Здесь необходимо обратить внимание, что после добавления FooterView или HeaderView мы не можем получить наш пользовательский адаптер через listview.getAdapter(),我们必须 следовать следующим шагам:
PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
См.
1.PagingListView
Это все содержимое статьи, мы надеемся, что это поможет вам в изучении, и希望大家多多支持呐喊教程。
Заявление: содержимое этой статьи взято из Интернета, авторские права принадлежат соответствующему владельцу, контент предоставлен пользователями Интернета, веб-сайт не имеет права собственности, не был обработан вручную, и не несет ответственности за связанные с этим юридические вопросы. Если вы обнаружите материалы,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @) для сообщения о нарушении,并提供 соответствующие доказательства. При подтверждении факта нарушения сайт незамедлительно удаляет материалы,涉嫌侵权.