-
Notifications
You must be signed in to change notification settings - Fork 728
Kotlin Model Examples
Epoxy models can be written in Kotlin easily. It is recommended to use either the @ModelView
or the ViewHolder
approach with Kotlin - use @ModelView
if you like custom views or have more complicated views.
This page assumes you already understand Epoxy and have read the rest of the wiki. This only adds additional context on top of that for how Kotlin usage can differ.
Here are some ideas for using Kotlin when writing models. You can adapt these and create your own patterns as well. See the kotlinsample module for more code.
The ModelView annotation is used on Views to have models generated from those views. This is pretty straightforward with Kotlin, but properties need some special handling.
@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
class KotlinView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
@ModelProp
fun setValue(value: Int) {
// Do something here with value
}
// Or if you need to store data in properties there are two options
// Can make it nullable like this and annotate the setter
var myListener: View.OnClickListener? = null
@CallbackProp set
// Or use lateinit
@TextProp lateinit var myText: CharSequence
@AfterPropsSet
fun useProps() {
// This is optional, and is called after the annotated properties above are set.
// This is useful for using several properties in one method to guarantee they are all set first.
}
}
This more traditional style uses an Epoxy view holder pattern. The KotlinHolder is used to cache the view look ups, and uses property delegates to simplify the process.
The annotations allow for code generation of a subclass, which has equals/hashcode, and some other helpers. An extension function is also generated to make it easier to use this in an EpoxyController.
The holder code is provided as a sample, and you can copy it into your project and modify as needed.
@EpoxyModelClass(layout = R.layout.view_holder_page_header)
abstract class SampleKotlinModelWithHolder : EpoxyModelWithHolder<Holder>() {
@EpoxyAttribute lateinit var title: String
@EpoxyAttribute lateinit var imageUrl: Uri
override fun bind(holder: Holder) {
holder.imageView.setImageURI(imageUrl)
holder.titleView.text = title
}
}
class Holder : KotlinHolder() {
val titleView by bind<TextView>(R.id.title)
val imageView by bind<ImageView>(R.id.image)
}
Not officially supported
This approach uses a more idiomatic Kotlin approach and does not require annotations or annotation processing. The data class is required to generate equals/hashcode which Epoxy needs for diffing. Views are easily declared right in the model via property delegates, so no view holder class is needed.
The downside is that views are looked up again every time the model is bound to a view, so it is not very efficient. Because of that this isn't recommended for performant apps, but is provided as a starting point or idea for what could be possible if you want to adapt it.
If you don't understand Epoxy well, or don't understand Kotlin well enough to know what this code is doing then it is recommended that you use either the @ModelView
or ViewHolder approach instead.
This is based on the KotlinModel sample that you can copy into your project and adapt.
The downside is you lose some functionality that the generated code provides (like the extension function for use in the EpoxyController)
data class SampleKotlinModel(
val title: String,
val imageUrl: Uri
) : KotlinModel(R.layout.view_holder_page_header) {
val titleView by bind<TextView>(R.id.title)
val imageView by bind<ImageView>(R.id.image)
override fun bind() {
titleView.text = title
imageView.setImageURI(imageUrl)
}
}