No one likes repeatable code. Nevertheless, there are constructions that have taken root and rooted in programming for quite a long time, in spite of this very repeatability.
There is such a commonly used construction of data binding in android:
fun bindCell1(view: View, data: Data) { view.cell1_text.setText(data.titleId) view.cell1_icon.setImageResource(data.icon) }
The obvious method, which has one very annoying negligence to me - each time you need to specify links view. and data. Each line contains 10 characters that are obvious.
And Kotlin has a way to get around this carelessness - Extensions. You can read more about them
here .
Brief summary of the articleIn this article I am not going to impart a certain programming style to someone. I just want to show the language capabilities of Kotlin using the example of a common task. You can solve this problem as you like, even without using Kotlin. Please refrain from comments on holivars - this is a purely technical article.
We implement the method as an extension for the data class.
We transform our design into
')
fun Data.bindCell1(view: View) { view.cell1_icon.setImageResource(icon) view.cell2_text.setText(titleId) }
How is it going? The bindSome method is no longer on its own, but is an extension for the data class. It turns out that this method behaves like the method of the Data class itself. There is one restriction - protected and private entities are not visible in extensions - which is logical, since in reality extension is not registered in the class itself. However, by combining the internal and public properties, you can get fairly secure combinations. Accordingly, it is now possible to contact directly the properties of the Data instance itself.
Now let's try to get rid of the
view prefix
. . To do this, create an immutable property.
val Data.bindMethod_cell_2: View.() -> Unit get() = { cell2_icon.setImageResource(icon) cell2_text.setText(titleId) }
How so?
Now the bindMethod property is an extension for the MediaData class, and at the same time according to the type of data - the extension for the View!
And what's next?
And then we can call this construct as a normal method, while passing the View as an argument!
data.bindMethod(view)
And if we go even further, we can pass View. () -> Unit as an argument.
What does this give us?
For example, we can not type the RecyclerView object from a word in general, passing only the layout ID and the resulting binding function to it. At the very beginning, the bindSome function (view: View, data: Data) was strongly typed, but now we don’t depend on this data type at all. - data type (View. () -> Unit) is bound only to the View.
And the intersection of namespaces?
It happens when the property names inside the View and Data are the same. Technically, all this just costs (you can add a prefix to the name of the layouts), but you can go a simple way:
val Data.bindMethod_cell_1: View.() -> Unit get() = { this.cell1_icon.setImageResource(this@bindMethod_cell_1.icon) this.cell1_text.setText(this@bindMethod_cell_1.titleId) }
Is that the design came out longer.
But what about the arguments?
If bindMethod has arguments, when calling this method, the View object is passed as the first argument, then the rest of the arguments are passed, as we usually call.
val Data.bindMethod: (View.(Int, String)->Unit) get() = { intValue, str -> view.numText.text = str.replace("%s", intValue.toString()) } //
This method will allow us to collect all the methods of binding in one place, and do, for example, like this:
Example of separation into separate documents class Data( val name:String, val icon:String)
Note that carAdapter and motoAdapter are not inside the Data class. they can be anywhere anywhere - if you want, take it out in extensions, you want, leave it with the class. You can call them from anywhere, extensions are imported as classes.
The materials used in the article, I compiled into a
small project