Created
September 10, 2020 13:39
-
-
Save strooooke/0d8b0f43a15bf767f3178964e7afe984 to your computer and use it in GitHub Desktop.
BoundFunction - directly binding callbacks for RecyclerView items
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Using ListAdapter for the shorter example; the same holds for any use of (Async)ListDiffer - the point here | |
// is about easy implementation of [areContentsTheSame] by just using equality on data class items. | |
// The expectation here is that updates to the item list result in the correct adapter notifications (and resulting | |
// animations). | |
class ExampleAdapter : ListAdapter<ExampleViewHolder, ExampleItem>(ExampleDiffCallback) { | |
object ExampleDiffCallback : DiffUtil.ItemCallback<ExampleItem> { | |
override fun areItemsTheSame(oldItem: ExampleItem, newItem: ExampleItem) = oldItem.id == newItem.id | |
override fun areContentsTheSame(oldItem: ExampleItem, newItem: ExampleItem) = oldItem == newItem | |
} | |
// skipped: onCreateViewHolder, onBindViewHolder etc. | |
class ExampleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | |
fun bind(item: ExampleItem) { | |
with(itemView) { | |
setText(item.name) | |
setOnClickListener { item.onClick() } | |
} | |
} | |
} | |
} | |
data class ExampleItem(val id: String, val name: String, val onClick: () -> Unit) | |
// First try: instantiated in presenter like | |
ExampleItem(thingy.id, thingy.name, { onItemClicked(thingy.id) }) | |
// And that, because of inequality of each separately instantiated lambda, does not result in the desired behaviour. | |
// Success: instantiate like | |
ExampleItem(thingy.id, thingy.name, BoundFunction1(this::onItemClicked, thingy.id)) | |
// and all is well. Note that this still relies on the method reference here - substituing `this::onItemClicked` by | |
// `{ onItemClicked(it }` would fail again for the same reason. | |
// And now, to build a pit of success here, let's change the signature of ExampleItem to | |
data class ExampleItem(val id: String, val name: String, val onClick: BoundFunction1<String, Unit>) | |
// Not arguing here that this is a good (or even the best) architecture for this case - but it does have it's advantages, | |
// especially when many distinct item types in distinct states with several distinct callbacks are in play. So at least | |
// for this specific drawback, here's a fix. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A 1-arg function, with the argument already bound - supporting equality in a way that the otherwise-equivalent lambda expression | |
* does not. | |
* Example: | |
* | |
* ``` | |
* val aLambda = { this.someFun(1) } | |
* val anotherLambda = { this.someFun(1) } | |
* | |
* assertEquals(aLambda == anotherLambda, false) | |
* | |
* val aBoundFun = BoundFunction1(this::someFun, 1) | |
* val anotherBoundFun = BoundFunction1(this::someFun, 1) | |
* | |
* assertEquals(aBoundFun == anotherBoundFun, true) | |
* ``` | |
*/ | |
data class BoundFunction1<T, R>(val function: (T) -> R, val boundArg: T) : Function0<R> { | |
override fun invoke(): R{ | |
return function.invoke(boundArg) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment