Android Example App: Difference between revisions
Line 86: | Line 86: | ||
In the attach function we need to create our delegate functions and add the subscriptions to the model. | In the attach function we need to create our delegate functions and add the subscriptions to the model. | ||
===Create Delegate in the Presenter=== | ====Create Delegate in the Presenter==== | ||
This is the function to call each time an observable is omitted | This is the function to call each time an observable is omitted | ||
<syntaxhighlight lang="kotlin"> | <syntaxhighlight lang="kotlin"> |
Revision as of 05:46, 19 March 2021
Introduction
I decided to base my application on the CLEAN architecture and set about looking for great examples.
I found Lopez at https://github.com/lopspower/CleanRxArchitecture which had all of the features I looking for
- Retrofit2
- Room
- RxJava
Implementing Screens
The Model
The model for the screens has data associated with each type of data to be displayed. With each construction pass the state and either the content type of data.
class CovidResultListViewModel(
val loadingState: LoadingState = LoadingState.NONE,
val contentState: ContentState = ContentState.NONE,
val covidResults: List<CovidResult>? = null,
var errorMessage: String? = null,
val snackMessage: String? = null) {
...
companion object {
fun createData(data: List<CovidResult>?) =
CovidResultListViewModel(contentState = ContentState.CONTENT, covidResults = data)
fun createLoading() =
CovidResultListViewModel(loadingState = LoadingState.LOADING, contentState = ContentState.CONTENT)
fun createError(error: String) =
CovidResultListViewModel(contentState = ContentState.ERROR, errorMessage = error)
...
}
}
The Presenter
The Presenter has a render method which is responsible for rendering the data.
override fun render(viewModel: CovidResultListViewModel) {
showLoading(viewModel.loadingState == LoadingState.LOADING)
showRefreshingLoading(binding.swipeRefreshLayout, false)
showRetryLoading(viewModel.loadingState == LoadingState.RETRY)
showContent(binding.content, viewModel.contentState == ContentState.CONTENT)
showError(viewModel.contentState == ContentState.ERROR)
renderData(viewModel.covidResults)
renderError(viewModel.errorMessage)
renderSnack(viewModel.snackMessage)
}
Each of the show methods set the flag appropriately for the data to be displayed.
protected fun showLoading(visible: Boolean) {
binding.progressLayoutId.progress.visibility = if (visible) View.VISIBLE else View.GONE
}
protected fun showRefreshingLoading(swipeRefreshLayout: SwipeRefreshLayout, visible: Boolean) {
swipeRefreshLayout.isRefreshing = visible
}
protected fun showRetryLoading(visible: Boolean) {
binding.errorLayoutId.btnErrorRetry.isClickable = !visible
binding.progressLayoutId.progress.visibility = if (visible) View.VISIBLE else View.INVISIBLE
}
Use Case Example Refresh
We want to allow the user to refresh the data. In our example it doesn't make much sense as the Covid Results only change daily but hopefully you get the idea.
When the user pulls down on the list of countries we want the screen to
- start showing the SwipeRefreshLayout
- load new data
- stop showing the SwipeRefreshLayout
To achieve this we need to
- Create Hook in view
- Create Delegate in Presenter
Create Hook in the View (Fragment)
In the View (Fragment) hook up the pulling of the recylerview to the view (fragment)
override fun intentRefreshData(): Observable<Boolean> =
binding.swipeRefreshLayout.refreshes().map { true }
Attach
In the attach function we need to create our delegate functions and add the subscriptions to the model.
Create Delegate in the Presenter
This is the function to call each time an observable is omitted
val refreshDataDelegate =
view.intentRefreshData().flatMap {
refreshCountryResultPresenter()
}
Add Subscription to the Model for the Delegate
We need to add the delegate to the View Model.
subscribeViewModel(view,
refreshCovidResultListDelegate,
loadCountryPreferencesDelegate,
removeFavoriteCountryChoiceDelegate,
)
Create function in Presenter
This is the function to call and is responsible for
- Calling the use use with the request
- Managing the repsonse in the UI
Refreshing
Android provides the SwipeRefreshLayout which is demonstrated below
Quite liked the approach of on by default. To implement this we
- Wrap the RecyclerView in it
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/covid_result_recycler_view"
android:layout_width="match_parent"
...
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>