-
-
Save Swisyn/0f91875c137b25a765c0e3f550d51d93 to your computer and use it in GitHub Desktop.
View binding extensions + delegate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Инфлейт ViewBinding заданного типа [T]. | |
* | |
* В качестве родителя используется [ViewGroup], по умолчанию view прикрепляется к корню родителя. | |
* **ВАЖНО!** Для инфлейта вьюх с `merge` в корне нужно использовать только этот метод. | |
*/ | |
inline fun <reified T : ViewBinding> ViewGroup.inflateViewBinding( | |
context: Context = this.context, | |
attachToRoot: Boolean = true | |
): T { | |
return T::class.inflate(LayoutInflater.from(context), this, attachToRoot) | |
} | |
/** | |
* Инфлейт ViewBinding заданного типа [T]. | |
* | |
* Метод для случая если нет [ViewGroup] или готового [LayoutInflater], в этом случае можно передать | |
* контекст из которого будет получен [LayoutInflater]. | |
*/ | |
inline fun <reified T : ViewBinding> Context.inflateViewBinding( | |
parent: ViewGroup? = null, | |
attachToRoot: Boolean = parent != null | |
): T { | |
return T::class.inflate(LayoutInflater.from(this), parent, attachToRoot) | |
} | |
/** | |
* Инфлейт ViewBinding заданного типа [T], с использованием заданного [LayoutInflater]. | |
* @sample com.openbank.library.dialog.CommonDialog.onCreateView | |
*/ | |
inline fun <reified T : ViewBinding> LayoutInflater.inflateViewBinding( | |
parent: ViewGroup? = null, | |
attachToRoot: Boolean = parent != null | |
): T { | |
return T::class.inflate(this, parent, attachToRoot) | |
} | |
/** | |
* Динамический вызов метода inflate у ViewBinding. | |
* | |
* При помощи этого метода можно вызвать inflate у любого ViewBinding, что делает возможным | |
* упрощение этого вызова. | |
* @see inflateViewBinding | |
*/ | |
fun <T : ViewBinding> KClass<T>.inflate( | |
inflater: LayoutInflater, | |
parent: ViewGroup?, | |
attachToRoot: Boolean | |
): T { | |
val inflateMethod = java.getInflateMethod() | |
@Suppress("UNCHECKED_CAST") | |
return if (inflateMethod.parameterTypes.size > 2) { | |
inflateMethod.invoke(null, inflater, parent, attachToRoot) | |
} else { | |
if (!attachToRoot) Log.d("ViewBinding", "attachToRoot is always true for ${java.simpleName}.inflate") | |
inflateMethod.invoke(null, inflater, parent) | |
} as T | |
} | |
private val inflateMethodsCache = mutableMapOf<Class<out ViewBinding>, Method>() | |
private fun Class<out ViewBinding>.getInflateMethod(): Method { | |
return inflateMethodsCache.getOrPut(this) { | |
declaredMethods.find { method -> | |
val parameterTypes = method.parameterTypes | |
method.name == "inflate" && | |
parameterTypes[0] == LayoutInflater::class.java && | |
parameterTypes.getOrNull(1) == ViewGroup::class.java && | |
(parameterTypes.size == 2 || parameterTypes[2] == Boolean::class.javaPrimitiveType) | |
} ?: error("Method ${this.simpleName}.inflate(LayoutInflater, ViewGroup[, boolean]) not found.") | |
} | |
} | |
/** | |
* Получение биндинга заданного типа [T] из [View]. | |
*/ | |
inline fun <reified T : ViewBinding> View.getBinding(): T = T::class.bind(this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-keepclassmembers class ** implements androidx.viewbinding.ViewBinding { | |
public static *** inflate(...); | |
public static *** bind(***); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Делегат, который возвращает ViewBinding. ViewBinding пересоздаётся при каждом пересоздании View фрагмента. | |
* | |
* Пример использования: | |
* ``` | |
* class MyFragment : Fragment(R.layout.my_fragment) { | |
* private val binding: MyFragmentBinding by viewBinding() | |
* } | |
* ``` | |
* Если попытаться получить значение до onViewCreated или после onViewDestroy, будет выброшено исключение. | |
*/ | |
inline fun <reified T : ViewBinding> Fragment.viewBinding(): ViewBindingDelegate<T> { | |
return ViewBindingDelegate(this, T::class) | |
} | |
/** @see viewBinding */ | |
class ViewBindingDelegate<T : ViewBinding> @PublishedApi internal constructor( | |
private val fragment: Fragment, | |
private val viewBindingClass: KClass<T> | |
) : ReadOnlyProperty<Any?, T> { | |
private var binding: T? = null | |
init { | |
fragment.viewLifecycleOwnerLiveData.observe(fragment) { lifecycleOwner -> | |
lifecycleOwner.lifecycle.addObserver( | |
LifecycleEventObserver { _, event -> | |
if (event == Lifecycle.Event.ON_DESTROY) binding = null | |
} | |
) | |
} | |
} | |
override fun getValue(thisRef: Any?, property: KProperty<*>): T = binding ?: obtainBinding() | |
private fun obtainBinding(): T { | |
val view = checkNotNull(fragment.view) { | |
"ViewBinding is only valid between onCreateView and onDestroyView." | |
} | |
return viewBindingClass.bind(view) | |
.also { binding = it } | |
} | |
} | |
/** | |
* Динамический вызов метода bind у ViewBinding. | |
* | |
* При помощи этого метода можно вызвать bind у любого ViewBinding, что делает возможным | |
* упрощение этого вызова. | |
* @see getBinding | |
*/ | |
fun <T : ViewBinding> KClass<T>.bind(rootView: View): T { | |
val inflateMethod = java.getBindMethod() | |
@Suppress("UNCHECKED_CAST") | |
return inflateMethod.invoke(null, rootView) as T | |
} | |
private val bindMethodsCache = mutableMapOf<Class<out ViewBinding>, Method>() | |
private fun Class<out ViewBinding>.getBindMethod(): Method { | |
return bindMethodsCache.getOrPut(this) { getDeclaredMethod("bind", View::class.java) } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment