diff --git a/README.md b/README.md index 9a478e7..55823c6 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A collection of modular elements for `RecyclerView` lists, alternative to [Google's Paging library](https://developer.android.com/topic/libraries/architecture/paging/), designed in Kotlin with these goals in mind: ```kotlin -implementation("com.otaliastudios:elements:0.4.0") +implementation("com.otaliastudios:elements:0.5.0") ``` Design features: diff --git a/build.gradle.kts b/build.gradle.kts index 070f9ec..a6ba9f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ buildscript { extra["minSdkVersion"] = 14 - extra["compileSdkVersion"] = 29 - extra["targetSdkVersion"] = 29 + extra["compileSdkVersion"] = 30 + extra["targetSdkVersion"] = 30 repositories { mavenCentral() @@ -11,8 +11,8 @@ buildscript { } dependencies { - classpath("com.android.tools.build:gradle:4.0.1") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0") + classpath("com.android.tools.build:gradle:4.1.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20") classpath("com.otaliastudios.tools:publisher:0.3.3") } } diff --git a/docs/_about/changelog.md b/docs/_about/changelog.md index ace1298..a9fed20 100644 --- a/docs/_about/changelog.md +++ b/docs/_about/changelog.md @@ -9,9 +9,16 @@ New versions are released through GitHub, so the reference page is the [GitHub R > You can [support development](https://github.com/natario1/Elements/issues/4) through the GitHub Sponsors program. Companies can share a tiny part of their revenue and get private support hours in return. Thanks! +### v0.5.0 + +- New: Presenter.onUnbind, to release resources acquired during onBind [#12][12] +- New: Presenter.onAttach and Presenter.onDetach to understand when the view is attached to the hierarchy [#12][12] + + + ### v0.4.0 -- Refreshed dependencies, Kotlin 1.4.0 [#10][10] +- Refreshed dependencies, Kotlin 1.4.0 [#10][10] @@ -22,3 +29,4 @@ First versioned release. [natario1]: https://github.com/natario1 [10]: https://github.com/natario1/Elements/pull/10 +[12]: https://github.com/natario1/Elements/pull/12 diff --git a/docs/_config.yml b/docs/_config.yml index 9cc8069..8f4f0c9 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -12,7 +12,7 @@ google_analytics_id: 'UA-155077779-6' google_site_verification: '4x49i17ABIrSvUl52SeL0-t0341aTnWWaC62-FYCRT4' github: [metadata] # TODO What's this? github_repo: Elements -github_version: 0.4.0 +github_version: 0.5.0 github_branch: master baseurl: '/Elements' # Keep as an empty string if served up at the root collections: diff --git a/docs/_docs/presenters.md b/docs/_docs/presenters.md index a94229e..e8211f1 100644 --- a/docs/_docs/presenters.md +++ b/docs/_docs/presenters.md @@ -16,7 +16,10 @@ These are the main tasks: |----|--------|-----------| |Holder creation|`onCreate(ViewGroup, Int)`|Here you must provide a `Holder` instance, typically inflating a layout resource.| |Holder initialization|`onInitialize(Holder, Int)`|The holder was created. You can perform here initialization task that do not depend on data (like color filters to icon), or add Views and object to the Holder cache using `Holder.set(key, data)` and `Holder.get(key)`.| -|Binding|`onBind(Page, Holder, Element)`|Bind data, contained in the given `Element`, to the view held by `Holder`.| +|Binding holder to data|`onBind(Page, Holder, Element)`|Bind data, contained in the given `Element`, to the view held by `Holder`.| +|Attaching holder to hierarchy|`onAttach(Holder)`|Called when holder is attached to the view hierarchy and is about to be visible.| +|Detaching holder from hierarchy|`onDetach(Holder)`|Called when holder is detached from the view hierarchy and is invisible.| +|Unbinding holder from data|`onUnbind(Holder)`|Called when the holder is unbound from the `Element` data. Can be used to release resources acquired during `onBind`.| Presenters also **accept a click listener** that will be automatically added to each view. The click listener will be added to the root view of the Holder, or, if found, to a child view that diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fd0c5a3..186b715 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/library/build.gradle.kts b/library/build.gradle.kts index cb034a0..2972f17 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -14,7 +14,7 @@ android { defaultConfig { setMinSdkVersion(property("minSdkVersion") as Int) setTargetSdkVersion(property("targetSdkVersion") as Int) - versionName = "0.4.0" + versionName = "0.5.0" } sourceSets { diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt b/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt index 1533ff0..3a967e8 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Adapter.kt @@ -338,7 +338,7 @@ public final class Adapter private constructor( @Suppress("UNCHECKED_CAST") val cast = data as LiveData>> var firstTime = true - cast.observe(this, Observer { + cast.observe(this, { if (it != null) { val sync = fromSavedState && firstTime onSourceResults(page, source, it, sync) @@ -409,7 +409,31 @@ public final class Adapter private constructor( } // We use the payloads version. - override fun onBindViewHolder(holder: Presenter.Holder, position: Int) {} + override fun onBindViewHolder(holder: Presenter.Holder, position: Int): Unit = Unit + + override fun onFailedToRecycleView(holder: Presenter.Holder): Boolean { + onUnbindViewHolder(holder) + return false + } + + override fun onViewRecycled(holder: Presenter.Holder) { + onUnbindViewHolder(holder) + } + + private fun onUnbindViewHolder(holder: Presenter.Holder) { + val presenter = typeMap.get(holder.itemViewType) + presenter.onUnbind(holder) + } + + override fun onViewAttachedToWindow(holder: Presenter.Holder) { + val presenter = typeMap.get(holder.itemViewType) + presenter.onAttach(holder) + } + + override fun onViewDetachedFromWindow(holder: Presenter.Holder) { + val presenter = typeMap.get(holder.itemViewType) + presenter.onDetach(holder) + } /** * Request a new page to be opened. diff --git a/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt b/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt index 994f2f2..12ff40e 100644 --- a/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt +++ b/library/src/main/kotlin/com/otaliastudios/elements/Presenter.kt @@ -113,6 +113,29 @@ public abstract class Presenter( } } + /** + * Called when the [Holder] is unbound from the element that was applied during [onBind]. + * Every [onBind] call is followed by an [onUnbind] call later on. This can be used to release + * resources. + */ + @UiThread + public open fun onUnbind(holder: Holder): Unit = Unit + + /** + * Called after [onBind], when the [Holder] is attached to the view hierarchy and is about to + * be visible to the user. + */ + @UiThread + public open fun onAttach(holder: Holder): Unit = Unit + + /** + * Called when the [Holder] is detached from the view hierarchy that was attached during [onAttach]. + * Every [onAttach] call is followed by an [onDetach] call later on. At this point the holder + * is not visible anymore. + */ + @UiThread + public open fun onDetach(holder: Holder): Unit = Unit + /** * Called to understand whether we should perform animations for the given animation type * and for the given holder. Presenters have fine grained control over what is animated and