-
Notifications
You must be signed in to change notification settings - Fork 730
Kotlin Model Examples
Epoxy models can be written in Kotlin easily. However, the annotations Epoxy was built with were designed to generate code as workarounds for Java limitations, which don't work as nicely in a Kotlin world.
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)
}
This approach uses a more idiomatic Kotlin approach and does not require annotations or annotation processing. The data class is required to generated 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.
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)
}
}