-
Notifications
You must be signed in to change notification settings - Fork 554
5.x | On Load More
- Introduction
- Automatic load more (with custom limits)
- Load more upon a user request (with custom limits)
- Load more per section
EndlessScroll / On Load More feature is achieved by an innovative mechanism with no ScrollListener implementation! The call is done in the onBindViewHolder()
of the Adapter, by checking the current position if matches with the last position minus the threshold*.
This elaboration is irrelevant compared to the usual callback for the ScrollListener implementation, especially if the progress item is not set.
*️⃣ = Set the threshold to anticipate the loading by calling
setEndlessScrollThreshold()
.
Two scenarios are foreseen for this feature:
- Automatic loading, where the trigger to load more items, is done automatically :-)
- Load more upon a user request, where the developer gives the possibility to the user to choose to load more items or not, by simply clicking an inner view in the item.
When there are no more items to load, the progressItem will be notified about the change: notifyItemChanged()
will be invoked before the removal. Optionally, the item can be removed after a custom delay to give time to read a possible message. In bindViewHolder()
of the item, you should be able to display the messages based on the result status.
There are two new criteria to establish automatically when to disable the endless feature and display a message in the progress item:
-
EndlessTargetCount
is the custom limit when the overall loaded items count met the target count set by the user. -
EndlessPageSize
is the final limit to stop further calls to the server when the last items count didn't reach the page size, which means all items are fully loaded.
ℹ️ Note: ProgressItem will be handled as Scrollable Footer Item, but always displayed between main items and others footers.
This feature is achieved by calling setEndlessScrollListener()
method.
You will have to provide a Listener and the progress item.
Setup with EndlessScrollListener
and with optional criteria to automatically stop loading more items.
mAdapter.setEndlessScrollListener(listener: activity, progressItem: mProgressItem)
.setEndlessScrollThreshold(numItems: 1) // Default=1
// (Optional) Automatically disable Endless feature:
.setEndlessPageSize(numItems: 7) // if newItems < 7
.setEndlessTargetCount(numItems: 27) // if totalItems >= 27
// New items inserted at the top
.setTopEndless(true); // default is false (bottom)
The layout displays a custom progress bar and a TextView* for the custom messages.
Check progress_item.xml layout to have something similar to:
image
The mapping might take in consideration the messages for statuses to handle special cases (see point 5): loading (progress indicator), noMoreLoad*, endlessDisabled*, onError*, onCancel*.
Check ProgressItem.java sample for more details. Here the main logic:
public enum StatusEnum {
MORE_TO_LOAD, // Default = should have an empty Payload
DISABLE_ENDLESS, // Endless is disabled because user has set limits
NO_MORE_LOAD, // Non-Empty payload = Payload.NO_MORE_LOAD
ON_CANCEL,
ON_ERROR
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, ProgressViewHolder holder,
int position, List payloads) {
Context context = holder.itemView.getContext();
holder.progressBar.setVisibility(View.GONE);
holder.progressMessage.setVisibility(View.VISIBLE);
if (!adapter.isEndlessScrollEnabled()) {
setStatus(StatusEnum.DISABLE_ENDLESS);
} else if (payloads.contains(Payload.NO_MORE_LOAD)) {
setStatus(StatusEnum.NO_MORE_LOAD);
}
switch (this.status) {
case NO_MORE_LOAD:
holder.progressMessage.setText(
context.getString(R.string.no_more_load_retry));
// Reset to default status for next binding
setStatus(StatusEnum.MORE_TO_LOAD);
break;
case DISABLE_ENDLESS:
holder.progressMessage.setText(context.getString(R.string.endless_disabled));
break;
case ON_CANCEL:
holder.progressMessage.setText(context.getString(R.string.endless_cancel));
// Reset to default status for next binding
setStatus(StatusEnum.MORE_TO_LOAD);
break;
case ON_ERROR:
holder.progressMessage.setText(context.getString(R.string.endless_error));
// Reset to default status for next binding
setStatus(StatusEnum.MORE_TO_LOAD);
break;
default:
holder.progressBar.setVisibility(View.VISIBLE);
holder.progressMessage.setVisibility(View.GONE);
break;
}
}
The onLoadMore()
callback will be invoked to load more items from the (local/remote) repository.
/**
* Loads more data.
* Use lastPosition and currentPage to know what to load next.
* lastPosition is the count of the main items without Scrollable Headers.
*/
@Override
public void onLoadMore(int lastPosition, int currentPage) {
// Preliminary check, left open to implement for the developer:
// - Normally, we don't want load more items when searching into the current
// Collection!
// - Alternatively, for a special filter, if we want load more items when
// filter is active, the new items that arrive from remote, should be
// already filtered, according to the searchText, before adding them to
// the Adapter!
if (mAdapter.hasSearchText()) {
mAdapter.onLoadMoreComplete(null);
return;
}
// Asynchronous call to Local or REST Api to load more items
callToRepositoryApi();
}
/**
* New callback from EndlessScrollListener when no more data to load.
* This method is called if any limit is reached (targetCount or pageSize
* must be set) AND if new data is temporary unavailable (ex. no connection or
* no new updates remotely). If no new data, a FlexibleAdapter#notifyItemChanged
* with a payload Payload#NO_MORE_LOAD is triggered on the progressItem.
*/
@Override
public void noMoreLoad(int newItemsSize) {
Log.d(TAG, "newItemsSize=" + newItemsSize);
Log.d(TAG, "Total pages loaded=" + mAdapter.getEndlessCurrentPage());
Log.d(TAG, "Total items loaded=" + mAdapter.getMainItemCount());
}
/**
* Display new items and notify user.
* This, instead, should be a Callback from the previous Asynchronous call.
*/
public void onLoadMoreComplete(final List<AbstractFlexibleItem> newItems) {
// Callback the Adapter to notify the change:
// - New items will be added to the end of the main list and progressItem
// will be hidden.
// - Instead, when list is null or empty, and limits are reached, Endless
// scroll will be disabled. To enable again, you must call
// setEndlessProgressItem(@Nullable T progressItem).
mAdapter.onLoadMoreComplete(newItems, 5000L)*;
// You can retrieve the new page number after adding new items!
Log.d(TAG, "EndlessCurrentPage=" + mAdapter.getEndlessCurrentPage());
Log.d(TAG, "EndlessPageSize=" + mAdapter.getEndlessPageSize());
Log.d(TAG, "EndlessTargetCount=" + mAdapter.getEndlessTargetCount());
// Notify user
...
}
*️⃣ = When list is
null
or empty, and limits are reached, endless scroll will be disabled. To enable again, you must set again the progress item by callingsetEndlessProgressItem(@Nullable T progressItem)
.
You will be handling the following cases:
-
onSuccess with items: call
onLoadMoreComplete(newItems)
- ProgressItem is removed immediately.
- Items are added to the end of the list.
- ProgressItem is removed immediately.
-
onSuccess no items: call
onLoadMoreComplete(empty list or null, delay*)
- Internal call and callback to
noMoreLoad()
.
- ProgressItem may display the updated message for a certain delay.
- Internal call and callback to
-
onCancel*: call
onLoadMoreComplete(empty list or null, delay*)
- Push the ON_CANCEL status to the ProgressItem.
- Internal call and callback to
noMoreLoad().
- ProgressItem may display the cancelled message for a certain delay.
- Push the ON_CANCEL status to the ProgressItem.
-
onError*: call
onLoadMoreComplete(empty list or null, delay*)
- Push the ON_ERROR status to the ProgressItem.
- Internal call and callback to
noMoreLoad()
.
- ProgressItem may display the error message for a certain delay.
- Push the ON_ERROR status to the ProgressItem.
*️⃣ = Optional delay with the following behaviours:
- Providing
any
delay, ProgressItem is kept to display the message, then automatically removed after the delay.
- Not providing
any
delay or0
delay, ProgressItem is removed immediately and when the user scrolls again, the item will be displayed again, a new call toonLoadMore()
is being triggered.
This feature is achieved by calling setEndlessProgressItem()
method that distinguishes the manual loading from the automatic loading by the fact that no listener is involved!
Setup with optional criteria to automatically stop loading more items.
mAdapter.setEndlessProgressItem(progressItem: mProgressItem)
.setEndlessScrollThreshold(numItems: 1) //Default=1
// Automatically disable Endless feature:
.setEndlessPageSize(numItems: 7) //if newItems < 7
.setEndlessTargetCount(numItems: 27); //if totalItems >= 27
// New items inserted at the top
.setTopEndless(true); //default is false (bottom)
The layout should support a Button to start the loading with onClick event; A custom progress bar that becomes visible when user taps the button, the button disappears; A TextView for the custom messages.
There's no listener involved... so the method onLoadMore()
won't be called.
Basically, you will have to handle click event of the button to load more data when user taps on it.
When the remote call receives the response/items, you will invoke onLoadMoreComplete(newItems)
as you normally would do for an automatic loading.
You will have to handle all the steps for manual loading PLUS the addition and the removal of the progress item by yourself as last element of the section.
Note: This cannot be reached by the library since it is a custom event inside your custom item.
- Update Data Set
- Selection modes
- Headers and Sections
- Scrollable Headers and Footers
- Expandable items
- Drag&Drop and Swipe
- EndlessScroll / On Load More
- Search Filter
- FastScroller
- Adapter Animations
- Third party Layout Managers
- Payload
- Smooth Layout Managers
- Flexible Item Decoration
- Utils
- ActionModeHelper
- AnimatorHelper
- EmptyViewHelper
- UndoHelper
* = Under revision!