Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@gpeal
Created October 3, 2018 17:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gpeal/14d9644387631f96fb7e2dec3d56e052 to your computer and use it in GitHub Desktop.
Save gpeal/14d9644387631f96fb7e2dec3d56e052 to your computer and use it in GitHub Desktop.
package com.yourapp
import android.support.annotation.IdRes
import android.support.v4.app.Fragment
import android.view.View
import android.view.ViewGroup
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
private object UNINITIALIZED
@Suppress("unused")
fun <T : View, F> F.bindView(@IdRes resId: Int) where F : Fragment, F : ViewBindingOwner = FragmentViewBinder<T, F>(resId)
class FragmentViewBinder<T : View, F>(@IdRes private val resId: Int) : ReadOnlyProperty<F, T> where F : Fragment, F : ViewBindingOwner {
private var view: Any? = UNINITIALIZED
override fun getValue(thisRef: F, property: KProperty<*>): T {
if (view == UNINITIALIZED) {
view = thisRef.view?.findViewById(resId) ?: throw IllegalStateException("Unable to find view for ${thisRef.resources.getResourceName(resId)}")
assertCorrectType(thisRef)
thisRef.addViewBinder(this)
}
@Suppress("UNCHECKED_CAST")
return view as T
}
fun clear() {
view = UNINITIALIZED
}
private fun assertCorrectType(thisRef: Fragment) {
@Suppress("UNCHECKED_CAST")
if (view as? T == null) {
val resName = thisRef.resources.getResourceName(resId)
throw IllegalStateException("Incorrect type for $resName")
}
}
}
/**
* Contains helpers to get butterknife-like support for Kotlin Fragments.
*
* To use, create a property like:
* private val myView: TextView by bindView(R.id.your_id)
*/
fun <T : View> bindView(@IdRes resId: Int) = object : ReadOnlyProperty<ViewGroup, T> {
private lateinit var view: Any
override fun getValue(thisRef: ViewGroup, property: KProperty<*>): T {
if (!::view.isInitialized) {
view = thisRef.findViewById(resId) ?: throw IllegalStateException("Unable to find view for ${thisRef.resources.getResourceName(resId)}")
assertCorrectType(thisRef)
}
@Suppress("UNCHECKED_CAST")
return view as T
}
private fun assertCorrectType(thisRef: ViewGroup) {
@Suppress("UNCHECKED_CAST")
if (view as? T == null) {
val resName = thisRef.resources.getResourceName(resId)
throw IllegalStateException("Incorrect type for $resName")
}
}
}
/**
* A Fragment should implement this if they want to use the bindView delegate.
*
* The Fragment should keep track of a list of ViewBinders as they are added then call clear on each of them in onDestroyView.
*/
interface ViewBindingOwner {
fun addViewBinder(binder: FragmentViewBinder<*, *>)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment