Skip to content

Instantly share code, notes, and snippets.

@iserbin
Forked from gpeal/FragmentA.kt
Created April 7, 2021 08:46
Show Gist options
  • Select an option

  • Save iserbin/85ab8e5cdd573a76e5bac7102ea14ef8 to your computer and use it in GitHub Desktop.

Select an option

Save iserbin/85ab8e5cdd573a76e5bac7102ea14ef8 to your computer and use it in GitHub Desktop.

Revisions

  1. @gpeal gpeal revised this gist Apr 1, 2021. 2 changed files with 7 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion FragmentViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -58,4 +58,7 @@ class FragmentViewBindingDelegate<T : ViewBinding>(
    binding = bindMethod.invoke(null, thisRef.requireView()).cast<T>()
    return binding!!
    }
    }
    }

    @Suppress("UNCHECKED_CAST")
    private fun <T> Any.cast(): T = this as T
    3 changes: 3 additions & 0 deletions ViewViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -22,3 +22,6 @@ class ViewBindingDelegate<T : ViewBinding>(

    override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T = binding
    }

    @Suppress("UNCHECKED_CAST")
    private fun <T> Any.cast(): T = this as T
  2. @gpeal gpeal revised this gist Mar 25, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ViewViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@ inline fun <reified T : ViewBinding> ViewGroup.viewBinding() = ViewBindingDelega

    class ViewBindingDelegate<T : ViewBinding>(
    private val bindingClass: Class<T>,
    val view: ViewGroup
    private val view: ViewGroup
    ) : ReadOnlyProperty<ViewGroup, T> {
    private val binding: T = try {
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.javaPrimitiveType)
  3. @gpeal gpeal revised this gist Mar 25, 2021. 1 changed file with 11 additions and 23 deletions.
    34 changes: 11 additions & 23 deletions ViewViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,3 @@
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import androidx.viewbinding.ViewBinding
    import com.tonal.trainer.utils.cast
    import kotlin.properties.ReadOnlyProperty
    import kotlin.reflect.KProperty

    /**
    * Create bindings for a view similar to bindView.
    *
    @@ -16,21 +9,16 @@ inline fun <reified T : ViewBinding> ViewGroup.viewBinding() = ViewBindingDelega

    class ViewBindingDelegate<T : ViewBinding>(
    private val bindingClass: Class<T>,
    val fragment: ViewGroup
    val view: ViewGroup
    ) : ReadOnlyProperty<ViewGroup, T> {
    private var binding: T? = null

    override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T {
    binding?.let { return it }

    try {
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.javaPrimitiveType)
    binding = inflateMethod.invoke(null, LayoutInflater.from(thisRef.context), thisRef, true).cast<T>()
    } catch (e: NoSuchMethodException) {
    // <merge> tags don't have the boolean parameter.
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java)
    binding = inflateMethod.invoke(null, LayoutInflater.from(thisRef.context), thisRef).cast<T>()
    }
    return binding!!
    private val binding: T = try {
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.javaPrimitiveType)
    inflateMethod.invoke(null, LayoutInflater.from(view.context), view, true).cast<T>()
    } catch (e: NoSuchMethodException) {
    // <merge> tags don't have the boolean parameter.
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java)
    inflateMethod.invoke(null, LayoutInflater.from(view.context), view).cast<T>()
    }
    }

    override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T = binding
    }
  4. @gpeal gpeal revised this gist Jan 17, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions FragmentA.kt
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    class WifiNetworksFragment : TonalFragment(R.layout.wifi_networks_fragment) {
    // This automatically creates and clears the binding in a lifecycle-aware way.
    private val binding: WifiNetworksFragmentBinding by viewBinding()

    ...
  5. @gpeal gpeal revised this gist Jan 17, 2021. 2 changed files with 20 additions and 20 deletions.
    20 changes: 10 additions & 10 deletions FragmentViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -10,6 +10,15 @@ import com.tonal.trainer.utils.cast
    import kotlin.properties.ReadOnlyProperty
    import kotlin.reflect.KProperty

    /**
    * Create bindings for a view similar to bindView.
    *
    * To use, just call
    * private val binding: FHomeWorkoutDetailsBinding by viewBinding()
    * with your binding class and access it as you normally would.
    */
    inline fun <reified T : ViewBinding> Fragment.viewBinding() = FragmentViewBindingDelegate(T::class.java, this)

    class FragmentViewBindingDelegate<T : ViewBinding>(
    private val bindingClass: Class<T>,
    val fragment: Fragment
    @@ -49,13 +58,4 @@ class FragmentViewBindingDelegate<T : ViewBinding>(
    binding = bindMethod.invoke(null, thisRef.requireView()).cast<T>()
    return binding!!
    }
    }

    /**
    * Create bindings for a view similar to bindView.
    *
    * To use, just call
    * private val binding: FHomeWorkoutDetailsBinding by viewBinding()
    * with your binding class and access it as you normally would.
    */
    inline fun <reified T : ViewBinding> Fragment.viewBinding() = FragmentViewBindingDelegate(T::class.java, this)
    }
    20 changes: 10 additions & 10 deletions ViewViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,15 @@ import com.tonal.trainer.utils.cast
    import kotlin.properties.ReadOnlyProperty
    import kotlin.reflect.KProperty

    /**
    * Create bindings for a view similar to bindView.
    *
    * To use, just call:
    * private val binding: FHomeWorkoutDetailsBinding by viewBinding()
    * with your binding class and access it as you normally would.
    */
    inline fun <reified T : ViewBinding> ViewGroup.viewBinding() = ViewBindingDelegate(T::class.java, this)

    class ViewBindingDelegate<T : ViewBinding>(
    private val bindingClass: Class<T>,
    val fragment: ViewGroup
    @@ -24,13 +33,4 @@ class ViewBindingDelegate<T : ViewBinding>(
    }
    return binding!!
    }
    }

    /**
    * Create bindings for a view similar to bindView.
    *
    * To use, just call:
    * private val binding: FHomeWorkoutDetailsBinding by viewBinding()
    * with your binding class and access it as you normally would.
    */
    inline fun <reified T : ViewBinding> ViewGroup.viewBinding() = ViewBindingDelegate(T::class.java, this)
    }
  6. @gpeal gpeal created this gist Jan 17, 2021.
    17 changes: 17 additions & 0 deletions FragmentA.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    class WifiNetworksFragment : TonalFragment(R.layout.wifi_networks_fragment) {
    private val binding: WifiNetworksFragmentBinding by viewBinding()

    ...
    }

    class WifiNetworkView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
    ) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
    // This does the inflate too.
    private val binding: WifiNetworkViewBinding by viewBinding()

    ...
    }
    61 changes: 61 additions & 0 deletions FragmentViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,61 @@
    import android.os.Handler
    import android.os.Looper
    import android.view.View
    import androidx.fragment.app.Fragment
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.LifecycleObserver
    import androidx.lifecycle.OnLifecycleEvent
    import androidx.viewbinding.ViewBinding
    import com.tonal.trainer.utils.cast
    import kotlin.properties.ReadOnlyProperty
    import kotlin.reflect.KProperty

    class FragmentViewBindingDelegate<T : ViewBinding>(
    private val bindingClass: Class<T>,
    val fragment: Fragment
    ) : ReadOnlyProperty<Fragment, T> {
    private val clearBindingHandler by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
    private var binding: T? = null

    private val bindMethod = bindingClass.getMethod("bind", View::class.java)

    init {
    fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
    viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
    // Lifecycle listeners are called before onDestroyView in a Fragment.
    // However, we want views to be able to use bindings in onDestroyView
    // to do cleanup so we clear the reference one frame later.
    clearBindingHandler.post { binding = null }
    }
    })
    }
    }

    override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
    // onCreateView may be called between onDestroyView and next Main thread cycle.
    // In this case [binding] refers to the previous fragment view. Check that binding's root view matches current fragment view
    if (binding != null && binding?.root !== thisRef.view) {
    binding = null
    }
    binding?.let { return it }

    val lifecycle = fragment.viewLifecycleOwner.lifecycle
    if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
    error("Cannot access view bindings. View lifecycle is ${lifecycle.currentState}!")
    }

    binding = bindMethod.invoke(null, thisRef.requireView()).cast<T>()
    return binding!!
    }
    }

    /**
    * Create bindings for a view similar to bindView.
    *
    * To use, just call
    * private val binding: FHomeWorkoutDetailsBinding by viewBinding()
    * with your binding class and access it as you normally would.
    */
    inline fun <reified T : ViewBinding> Fragment.viewBinding() = FragmentViewBindingDelegate(T::class.java, this)
    36 changes: 36 additions & 0 deletions ViewViewBindingDelegate.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import androidx.viewbinding.ViewBinding
    import com.tonal.trainer.utils.cast
    import kotlin.properties.ReadOnlyProperty
    import kotlin.reflect.KProperty

    class ViewBindingDelegate<T : ViewBinding>(
    private val bindingClass: Class<T>,
    val fragment: ViewGroup
    ) : ReadOnlyProperty<ViewGroup, T> {
    private var binding: T? = null

    override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T {
    binding?.let { return it }

    try {
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.javaPrimitiveType)
    binding = inflateMethod.invoke(null, LayoutInflater.from(thisRef.context), thisRef, true).cast<T>()
    } catch (e: NoSuchMethodException) {
    // <merge> tags don't have the boolean parameter.
    val inflateMethod = bindingClass.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java)
    binding = inflateMethod.invoke(null, LayoutInflater.from(thisRef.context), thisRef).cast<T>()
    }
    return binding!!
    }
    }

    /**
    * Create bindings for a view similar to bindView.
    *
    * To use, just call:
    * private val binding: FHomeWorkoutDetailsBinding by viewBinding()
    * with your binding class and access it as you normally would.
    */
    inline fun <reified T : ViewBinding> ViewGroup.viewBinding() = ViewBindingDelegate(T::class.java, this)