English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Thoughts
Today, let's implement a ListView with pull-to-load more function. GitHub link:PulmListView, welcome to fork && star.
Let's first organize our thoughts. If we want to implement a ListView with pull-to-load more function, the functions we need to implement include:
1.A custom ListView, and this ListView can judge whether it is at the bottom.
2.A custom FooterView, used for UI display during the process of loading more in ListView.
3.Associate FooterView and ListView, including judgment of loading timing, display and hide of FooterView.
4.Provide an interface for loading more to facilitate the callback implementation of the user's actual loading more function.
5.Provide a callback method for loading more to add the latest user data and update related status tags and UI display.
Regarding the above5function, we will analyze the corresponding implementation method one by one.
Function1(Custom ListView)
We can inherit ListView to implement a custom PulmListView.
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 is not enough to just implement the three constructors of ListView, we need ListView to be able to judge whether the current ListView has scrolled to the last element.
To determine whether the scroll has reached the last element, we can achieve this by setting OnScrollListener for the ListView. The code is as follows:
private void init() { super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Invoke the OnScrollListener set by the user if (mUserOnScrollListener != null) { mUserOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // Invoke the OnScrollListener set by the user 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 is the total number of elements contained in the ListView int lastVisibleItem = firstVisibleItem + visibleItemCount; if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) { if (mOnPullUpLoadMoreListener != null) {}} mIsLoading = true; mOnPullUpLoadMoreListener.onPullUpLoadMore(); } } } }); }
From the code comments, it can be known that, through (firstVisibleItem + visibleItemCount) can get the number of elements currently displayed on the screen. If the number of displayed elements is equal to the total number of elements in the ListView, then it can be considered that the ListView has scrolled to the bottom.
Function2(Custom FooterView)
Here we can implement a relatively simple FooterView, that is, the UI layout for loading more. For example, we can display a ProgressBar and a line of text. The specific code is as follows:
/** * The layout for the View that loads more, can be customized. */ 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); } }
Layout file:
<?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>
Function3(Link ListView and FooterView)
Firstly, we need to save FooterView in the ListView through a variable and instantiate it in the constructor.
private View mLoadMoreView; private void init() { mLoadMoreView = new LoadMoreView(getContext()); }
Secondly, we need to control the display and hide of FooterView. Consider the timing for the display and hide of FooterView:
•The display opportunity: The ListView is at the bottom and there is more data to load.
•The hidden opportunity: The ListView has finished loading more operations.
To determine whether there is more data to load, we need to define a boolean variable mIsPageFinished to indicate whether the data loading process has ended.
To ensure that only one data loading process is performed at a time, we still need to define a boolean variable mIsLoading to indicate whether the current data loading process is in progress.
The timing for displaying and hiding FooterView is clarified, along with variables to control the state, making the code implementation relatively easy.
Display timing:
private void init() { mIsLoading = false; // Not in a loading state when initializing mIsPageFinished = false; // By default, more data needs to be loaded when initializing mLoadMoreView = new LoadMoreView(getContext()); // Instantiate FooterView super.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Invoke the OnScrollListener set by the user if (mUserOnScrollListener != null) { mUserOnScrollListener.onScrollStateChanged(view, scrollState); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // Invoke the OnScrollListener set by the user if (mUserOnScrollListener != null) { mUserOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } int lastVisibleItem = firstVisibleItem + visibleItemCount; // When at the end of the ListView and more data needs to be loaded without any loading process in progress, perform the operation to load more data. if (!mIsLoading && !mIsPageFinished && lastVisibleItem == totalItemCount) { if (mOnPullUpLoadMoreListener != null) {}} mIsLoading = true; // Set the loading more in progress status to true showLoadMoreView(); // Display the loading more layout mOnPullUpLoadMoreListener.onPullUpLoadMore(); // Call the user-defined callback interface for loading more } } } }); } private void showLoadMoreView() { // Here, the root layout ID for loading more is set to id_load_more_layout, making it convenient for users to customize the loading more layout. if (findViewById(R.id.id_load_more_layout) == null) { addFooterView(mLoadMoreView); } }
Hidden timing:
/** * Callback method after loading more is finished. * * @param isPageFinished Whether pagination is finished * @param newItems Pagination loaded data * @param isFirstLoad Whether it is the first time to load data (used for configuring the pull-down refresh framework to avoid page flicker) */ public void onFinishLoading(boolean isPageFinished, List<63;> newItems, boolean isFirstLoad) { mIsLoading = false; // Mark that there are no more loading processes currently in execution setIsPageFinished(isPageFinished); // Set the page finished flag and remove FooterView } private void setIsPageFinished(boolean isPageFinished) { mIsPageFinished = isPageFinished; removeFooterView(mLoadMoreView); }
Function4(Callback interface for implementing loading more)
This is relatively simple, we define an interface to facilitate the callback of the user's actual implementation of loading more.
/** * Callback interface for pulling up to load more */ public interface OnPullUpLoadMoreListener { void onPullUpLoadMore(); } private OnPullUpLoadMoreListener mOnPullUpLoadMoreListener; /** * Set the callback interface for pulling up to load more. * @param l Callback interface for pulling up to load more */ public void setOnPullUpLoadMoreListener(OnPullUpLoadMoreListener l) { this.mOnPullUpLoadMoreListener = l; }
Function5(End callback for loading more)
In order to maintain the data set in PulmListView, it is necessary to customize an Adapter, use List to store the data set in the Adapter, and submit the methods for adding and deleting.
Custom Adapter:
/** * Abstract Adapter. */ 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 is an isFirstLoad variable added 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, it involves clearing the data collection of ListView and then adding all. If there is no isFirstLoad parameter, the user must update the ListView data collection in two steps when pulling down to refresh:
1.removeAllItems and then call notifyDataSetChanged.
2.addMoreItems and then call notifyDataSetChanged.
Continuous two times of notifyDataSetChanged may cause screen flicker, 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 Pagination loaded data * @param isFirstLoad Whether it is the first time to load data (used for configuring the pull-down refresh framework to avoid page flicker) */ public void onFinishLoading(boolean isPageFinished, List<63;> newItems, boolean isFirstLoad) { mIsLoading = false; setIsPageFinished(isPageFinished); // Add updated data if (newItems != null && newItems.size() > 0) { PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter(); adapter.addMoreItems(newItems, isFirstLoad); } }
Here, it should be noted that after adding FooterView or HeaderView, we cannot obtain our custom adapter through listview.getAdapter, and must follow the following steps:
PulmBaseAdapter adapter = (PulmBaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
Reference
1.PagingListView
That's all for this article. I hope it will be helpful to everyone's learning, and I also hope everyone will support the Shouting Tutorial more.
Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, does not undergo artificial editing, and does not bear relevant legal liabilities. If you find content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report, and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.)