Pull-to-Refresh/Load-more
The content of this chapter may seem extensive, but the code is actually concise. The detailed explanations are omitted to avoid redundancy.
SmartRefreshLayout is currently the most powerful refresh framework for Android in terms of extensibility. BRV's pull-to-refresh and load-more functionalities are built on top of SmartRefreshLayout, supporting all its features and adding new ones.
If you need more customization options for pull-to-refresh or load-more, please refer to the documentation of SmartRefreshLayout.
ThePageRefreshLayout
in this library inherits fromSmartRefreshLayout
, so it has all its features.
This library includes the following basic dependencies of SmartRefreshLayout, so there's no need to include them again:
api 'io.github.scwang90:refresh-layout-kernel:2.0.5'
api 'io.github.scwang90:refresh-header-material:2.0.5'
api 'io.github.scwang90:refresh-footer-classics:2.0.5'
To specify custom refresh headers and footers for SmartRefreshLayout, you need to include the respective libraries (that's how SmartRefreshLayout is designed):
Optional configurations for refresh headers and footers:
implementation 'io.github.scwang90:refresh-layout-kernel:2.0.5' // Core dependency (must include)
implementation 'io.github.scwang90:refresh-header-classics:2.0.5' // Classic refresh header
implementation 'io.github.scwang90:refresh-header-radar:2.0.5' // Radar refresh header
implementation 'io.github.scwang90:refresh-header-falsify:2.0.5' // Falsify refresh header
implementation 'io.github.scwang90:refresh-header-material:2.0.5' // Material refresh header
implementation 'io.github.scwang90:refresh-header-two-level:2.0.5' // Two-level refresh header
implementation 'io.github.scwang90:refresh-footer-ball:2.0.5' // Ball pulse load-more
implementation 'io.github.scwang90:refresh-footer-classics:2.0.5' // Classic load-more
Initialization¶
The refresh layout requires initialization first, and it's recommended to do it in the Application
class.
SmartRefreshLayout.setDefaultRefreshHeaderCreator { context, layout -> MaterialHeader(this) }
SmartRefreshLayout.setDefaultRefreshFooterCreator { context, layout -> ClassicsFooter(this) }
PageRefreshLayout¶
This control extends from SmartRefreshLayout
and adds the following features:
- Simplified functions
- Fine-tuned details
- Empty state
- Pagination loading
- Pull-to-load-more
- Pre-loading / Pre-pulling
Declaration¶
There are two ways to create a PageRefreshLayout
, but it's recommended to wrap it in a layout.
<com.drake.brv.PageRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/page"
app:stateEnabled="true"
android:layout_height="match_parent"
tools:context="com.drake.brv.sample.fragment.Refresh
Fragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.drake.brv.PageRefreshLayout>
It is only recommended to use layout creation, which can keep the project code readable and avoid unnecessary problems
val page = rv.pageCreate()
Create the List¶
rv.linear().setup {
addType<Model>(R.layout.item_simple)
addType<DoubleItemModel>(R.layout.item_full_span)
}
page.onRefresh {
postDelayed({
val data = getData()
addData(data) {
index < total
}
}, 2000)
}.autoRefresh()
onRefresh
is the function that will be executed every time the list is refreshed or loaded more.
Listen for State Changes¶
// Pull-to-refresh
page.onRefresh {
// You can perform network requests or other asynchronous operations here
}
// Load more
page.onLoadMore {
// You can perform network requests or other asynchronous operations here
}
- If you don't call
onLoadMore
, the pull-to-load-more functionality will also execute theonRefresh
function. In most cases, pull-to-refresh and load more use the same API but with different page values.
Trigger Refresh¶
There are three functions to trigger the refresh state (all of them will invoke the onRefresh
function):
Function | Description |
---|---|
autoRefresh | Show the pull-to-refresh animation |
showLoading | Show the loading state with a custom layout |
refresh | Refresh silently without animation |
refreshing | Equivalent to showLoading for the first time. After loading is complete, it's equivalent to refresh . |
All three trigger methods will reset the index to
startIndex
. The index is the default field for page increment. You will see how to use this field later.
Empty State¶
The PageRefreshLayout
contains a StateLayout
to handle the empty state.
Control the state of the empty state layout (usually handled automatically by the framework):
Function | Description |
---|---|
showLoading | Show the loading empty state |
showEmpty | Show the empty empty state |
showError | Show the error empty state |
showContent | Show the content view |
Global Empty State Configuration¶
Global empty state configuration is shared with StateLayout
because PageRefreshLayout
embeds StateLayout
.
/**
* Recommended to configure the global empty state in the Application class.
* For more details, refer to https://github.com/liangjingkanji/StateLayout.
*/
StateConfig.apply {
emptyLayout = R.layout.layout_empty
errorLayout = R.layout.layout_error
loadingLayout = R.layout.layout_loading
onLoading {
// This callback is triggered when the loading layout is created. You can customize animations or click events here.
}
}
Singleton Empty State Configuration¶
Singleton empty state means that for a specific layout, you want to use a different empty state layout instead of the global configuration. You don't need to specify all of them; you can choose to specify only the loading or error layout.
<com.drake.brv.PageRefreshLayout
.....
app:error_layout="@layout/layout_error"
app:empty_layout="@layout/layout_empty"
app:loading_layout="@layout/layout_loading">
<!--RecyclerView code-->
</com.drake.brv.PageRefreshLayout>
page.apply {
loadingLayout = R.layout.layout_loading
emptyLayout = R.layout.layout_empty
// errorLayout = R.layout.layout_error
}
By default, the empty state will be used. If you
have set the global empty state and don't want to use it at this moment, you can use the stateEnabled
property or function.
As the header layout is part of the list and the empty state covers the entire list, if you want to use the empty state without affecting the header layout, you can use CoordinatorLayout
.
Note: Using
NestedScrollView
to nest the RecyclerView will load all items at once, which consumes more memory. However, nesting the RecyclerView inside aCoordinatorLayout
won't have this issue.
Refreshing Data¶
As mentioned earlier, PageRefreshLayout
supports automatic pagination. You don't need to use the rv.models
function to set the data; instead, you can use addData
.
pageLayout.onRefresh {
scope {
val data = Get<String>("/path").await()
addData(data.list) {
adapter.itemCount < data.count
}
}
}
In most cases, the backend defines the first page as 1, but sometimes it can be 0. You can set the initial value of the index
field (the field representing the page) in the onCreate
method of your Application
class.
class App : Application() {
override fun onCreate() {
super.onCreate()
PageRefreshLayout.startIndex = 1
}
}
In the code snippet above, I used another open-source project of mine, Net, for network requests. It can be configured together with BRV.
If
PageRefreshLayout
is not directly wrapping the RecyclerView, you need to pass theadapter
parameter to theaddData
function in order to use automatic pagination. Otherwise, an exception will be thrown.
Disable Pull-to-Refresh in Empty State¶
Control the PageRefreshLayout's ability to handle pull-to-refresh gestures when the empty state is displayed.
Function | Description |
---|---|
refreshEnableWhenEmpty | Enable pull-to-refresh when the empty state is displayed |
refreshEnableWhenError | Enable pull-to-refresh when the error state is displayed |
The corresponding companion object properties are used for global configuration, while the object properties of PageRefreshLayout are used for singleton configuration.
If you need more complex logic, it is recommended to use PageRefreshLayout.setEnableRefresh()
in the empty state callback to have more control.