androidbroadcast / ViewBindingPropertyDelegate

Make work with Android View Binding simpler
https://proandroiddev.com/make-android-view-binding-great-with-kotlin-b71dd9c87719
Apache License 2.0
1.42k stars 102 forks source link

LayoutViewBinding, for custom views #31

Closed premacck closed 3 years ago

premacck commented 3 years ago

Thanks for this wonderful library @kirich1409 ! I'm really enjoying working with the non-reflection delegates

However, I was tinkering around for some ways to make the bindings in custom views delegated

Before:

// layout XML: R.layout.layout_custom
class CustomLayout @JvmOverloads constructor(...) : FrameLayout(...) {
  private val binding = LayoutCustomBinding.inflate(LayoutInflater.from(context), this)
}

My Delegate property:

abstract class LayoutBindingProperty<in R : View, T : ViewBinding>(private val binder: (R) -> T) : ReadOnlyProperty<R, T> {

  private var viewBinding: T? = null

  protected abstract fun getLifecycleOwner(thisRef: R): LifecycleOwner?    // Don't know what to do with this, maybe some optimizations? Or maybe use OnAttachStateChangeListener?

  @MainThread override fun getValue(thisRef: R, property: KProperty<*>): T {
    viewBinding?.let { return it }
    val binding = binder(thisRef)
    viewBinding = binding
    return binding
  }
}

private class LayoutViewBindingProperty<V : View, T : ViewBinding>(
  viewBinder: (V) -> T
) : LayoutBindingProperty<V, T>(viewBinder) {
  override fun getLifecycleOwner(thisRef: V): LifecycleOwner? = thisRef.findViewTreeLifecycleOwner()    // Or ViewTreeLifecycleOwner.get(thisRef), from `lifecycle-*-ktx:2.3.0-rc01`
}

fun <V : View, T : ViewBinding> layoutViewBinding(binder: (V) -> T): LayoutBindingProperty<V, T> = LayoutViewBindingProperty(binder)

inline fun <V : ViewGroup, T : ViewBinding> V.viewBinding(crossinline vbFactory: (LayoutInflater, ViewGroup) -> T): LayoutBindingProperty<V, T> =
  layoutViewBinding { vbFactory(LayoutInflater.from(context), this) }

After using delegate property:

// layout XML: R.layout.layout_custom
class CustomLayout @JvmOverloads constructor(...) : FrameLayout(...) {
  private val binding by viewBinding(LayoutCustomBinding::inflate)
}

I mean this approach is working, but I'm thinking is there any way of optimizing this approach?

kirich1409 commented 3 years ago

Hi. I am working on the viewBinding delegate for ViewGroup and RecyclerView.ViewHolder and they will be part of the next release. The release will be on the next week. Your code is right. ViewGroup doesn't have any connected LifecycleOwner with it.

premacck commented 3 years ago

Hi @kirich1409 I was going through your example from ProfileView.kt to understand viewbinding in ViewGroups, and what I found curious was the need to use View.inflate(context, R.layout.view_profile, this) with the view binding delegate.

Like when I just do private val binding = ViewProfileBinding.inflate(LayoutInflater.from(context), this) I don't need to add View.inflate(...) anymore

Is there any way to inflate the ViewBinding in the view group through the delegate property only? If not yet, can it be implemented?

kirich1409 commented 3 years ago

The problem is that delegate is lazy and view will be attached on the first call of viewBinding delegate. I didn't add such kind of delegate because I wasn't sure about behavior.

premacck commented 3 years ago

Ah! I see what you mean, good catch, so even when using the delegate, we'll have to write binding in the init { } block anyways

kirich1409 commented 3 years ago

I have an idea about that and will implement it