Last active
December 28, 2021 13:34
-
-
Save slavonnet/4fca3c62f8d50d8dadd1687426415de0 to your computer and use it in GitHub Desktop.
Delegate for auto detect ViewBinding and Binding
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
// Helpers and utils | |
internal inline var View.isVisibleEx: Boolean | |
get() = isVisible | |
set(value) { | |
if (value != isVisible) isVisible = value | |
} | |
internal inline var View.isEnabledEx: Boolean | |
get() = isEnabled | |
set(value) { | |
if (value != isEnabled) isEnabled = value | |
} | |
internal inline var MenuItem.isVisibleEx: Boolean | |
get() = isVisible | |
set(value) { | |
if (value != isVisible) isVisible = value | |
} | |
internal inline var MenuItem.isEnabledEx: Boolean | |
get() = isEnabled | |
set(value) { | |
if (value != isEnabled) isEnabled = value | |
} | |
internal fun TextView.setFastText(text: String?) { | |
if (this.text.contentEquals(text)) | |
return | |
if (text.isNullOrEmpty()) { | |
if (!this.text.isNullOrEmpty()) { | |
MainScope().launch { | |
this@setFastText.post { | |
this@setFastText.text = null | |
} | |
} | |
} | |
return | |
} | |
if (!this.isVisible || !this.isAttachedToWindow) { | |
MainScope().launch { | |
this@setFastText.post { | |
this@setFastText.text = text | |
} | |
} | |
return | |
} | |
MainScope().launch { | |
val pre = withContext(Dispatchers.IO) { | |
PrecomputedTextCompat.create( | |
text, | |
TextViewCompat.getTextMetricsParams(this@setFastText) | |
) | |
} | |
this@setFastText.post { | |
kotlin.runCatching { TextViewCompat.setPrecomputedText(this@setFastText, pre) } | |
.exceptionOrNull()?.let { | |
Log.e( | |
"SSSS", | |
"Cant apply $text to ${this@setFastText}. Fallback to simple setText()" | |
) | |
this@setFastText.text = text | |
} | |
} | |
} | |
} | |
internal fun AppCompatTextView.setFastText(text: String?) = (this as? TextView)?.setFastText(text) | |
private inline fun <Z, reified T : ViewBinding> Z.bindViewBindingWrapper(): Lazy<T?> = | |
object : Lazy<T?> { | |
private val v: View? | |
get() = when (this@bindViewBindingWrapper) { | |
is RecyclerView.ViewHolder -> this@bindViewBindingWrapper.itemView | |
is FragmentActivity -> this@bindViewBindingWrapper.findViewById<ViewGroup>(android.R.id.content) | |
?.getChildAt(0) | |
is Fragment -> this@bindViewBindingWrapper.view | |
is View -> this@bindViewBindingWrapper | |
else -> null | |
} | |
private val life: LifecycleOwner? | |
get() = when (this@bindViewBindingWrapper) { | |
is Fragment -> this@bindViewBindingWrapper.viewLifecycleOwner | |
is View, is RecyclerView.ViewHolder -> | |
(v?.let { ViewTreeLifecycleOwner.get(it) } ?: v?.findViewTreeLifecycleOwner() | |
?: runCatching<Fragment?> { v?.findFragment() }.getOrNull() ?: | |
getActivityByContext(v?.context) as? FragmentActivity)?.let { | |
(it as? Fragment)?.viewLifecycleOwner ?: it | |
} | |
is FragmentActivity -> this@bindViewBindingWrapper | |
else -> null | |
} | |
private var binding: WeakReference<T?>? = null | |
override fun isInitialized(): Boolean = binding?.get() != null | |
private fun doCLear() { | |
// listOfNotNull(binding, owner).forEach { cls -> | |
// cls::class.java.superclass.declaredFields.asSequence().filterNotNull().filter { | |
// if (!it.isAccessible) | |
// it.isAccessible = true | |
// | |
// it.isAccessible && | |
// !Modifier.isProtected(it.modifiers) && | |
// Objects.nonNull(it.get(cls)) && | |
// !it::class.java.isPrimitive && | |
// it.get(cls).javaClass.isInstance(Objects::class.java) | |
// } | |
// .forEach { | |
// if (!it.isAccessible) | |
// it.isAccessible = true | |
// | |
// with(it.get(cls)) { | |
// (this as? Fragment)?.onDestroyView() | |
// (this as? ViewModelStoreOwner)?.viewModelStore?.clear() | |
// (this as? Fragment)?.childFragmentManager?.fragments?.clear() | |
// (this as? ViewGroup)?.removeAllViews() | |
// } | |
// it.set(cls, null) | |
// } | |
// } | |
if (this@bindViewBindingWrapper !is FragmentActivity) { | |
v?.allViews?.filter { it is ViewPager2 || it is RecyclerView }?.forEach { | |
(it as? RecyclerView)?.adapter = null | |
(it as? ViewPager2)?.adapter = null | |
(it.parent as? ViewGroup)?.removeAllViews() | |
} | |
(binding?.get() as? ViewDataBinding)?.lifecycleOwner = null | |
(binding?.get() as? ViewDataBinding)?.unbind() | |
(v as? ViewGroup)?.removeAllViews() | |
} | |
binding?.clear() | |
binding = null | |
} | |
override val value: T? | |
get() = binding?.get() ?: run { | |
life?.lifecycle?.currentState?.let { | |
it < Lifecycle.State.INITIALIZED && return@run null | |
} | |
v?.let { v -> | |
kotlin.runCatching { | |
when (T::class.java) { | |
is ViewDataBinding -> DataBindingUtil.bind(v) as T? | |
else -> T::class.java.getMethod("bind", View::class.java) | |
.invoke(null, v) as T? | |
} | |
}.getOrNull()?.let { b -> | |
binding?.clear() | |
binding = WeakReference(b) | |
life?.let { l -> | |
setDestroy(b, l) | |
} | |
} | |
} | |
binding?.get() | |
} | |
private fun setDestroy(b: T, l: LifecycleOwner) { | |
(b as? ViewDataBinding)?.lifecycleOwner = l | |
l.lifecycle.addObserver( | |
object : DefaultLifecycleObserver { | |
override fun onDestroy(owner: LifecycleOwner) { | |
owner.lifecycle.removeObserver(this) | |
doCLear() | |
super.onDestroy(owner) | |
} | |
} | |
) | |
} | |
} | |
@MainThread | |
internal inline fun <reified T : ViewBinding> FragmentActivity.bindViewBinding(): Lazy<T?> = | |
this@bindViewBinding.bindViewBindingWrapper() | |
@MainThread | |
internal inline fun <reified T : ViewBinding> Fragment.bindViewBinding(): Lazy<T?> = | |
this@bindViewBinding.bindViewBindingWrapper() | |
@MainThread | |
internal inline fun <reified T : ViewBinding> View.bindViewBinding(): Lazy<T?> = | |
this@bindViewBinding.bindViewBindingWrapper() | |
@MainThread | |
internal inline fun <reified T : ViewBinding> RecyclerView.ViewHolder.bindViewBinding(): Lazy<T?> = | |
this@bindViewBinding.bindViewBindingWrapper() | |
internal tailrec fun getActivityByContext(cxt: Context?): AppCompatActivity? = | |
when (cxt) { | |
is AppCompatActivity -> cxt | |
null -> null | |
else -> getActivityByContext((cxt as? android.content.ContextWrapper)?.baseContext) | |
} | |
internal fun View.hideKey() { | |
getActivityByContext(this.context)?.hideKey() | |
} | |
internal fun Fragment.hideKey() { | |
this.activity?.hideKey() | |
} | |
internal fun FragmentActivity.hideKey() { | |
(this as? AppCompatActivity)?.hideKey() | |
} | |
internal fun AppCompatActivity.hideKey() { | |
this.currentFocus?.let { | |
val imm = | |
this.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager | |
imm?.hideSoftInputFromWindow(this.window.decorView.windowToken, 0) | |
} | |
} | |
//////////////////////// | |
class StripOfRea;Adater : RecyclerView.Adapter | |
private var lifecycle: LifecycleOwner? = null | |
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { | |
super.onAttachedToRecyclerView(recyclerView) | |
kotlin.runCatching { | |
recyclerView.findFragment<Fragment>().viewLifecycleOwner.let { | |
lifecycle = it | |
} | |
recyclerView.findViewTreeLifecycleOwner()?.let { | |
lifecycle = it | |
} | |
} | |
} | |
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { | |
super.onViewAttachedToWindow(holder) | |
lifecycle?.let { | |
ViewTreeLifecycleOwner.set(holder.itemView, lifecycle) | |
} | |
} | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = | |
when (viewType) { | |
R.layout.a_main_f_ticket_list_item -> | |
SituationTicketViewHolder( | |
LayoutInflater.from(parent.context).inflate( | |
R.layout.a_main_f_ticket_list_item, | |
parent, | |
false | |
) | |
) | |
// .... | |
}.also { v -> | |
lifecycle?.let { | |
ViewTreeLifecycleOwner.set(v.itemView, lifecycle) | |
} | |
} | |
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { | |
lifecycle?.let { | |
ViewTreeLifecycleOwner.set(holder.itemView, lifecycle) | |
} | |
/// .... | |
} | |
} | |
// Bind Example in VH | |
nternal class ActiveCallsViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { | |
private val bind: ListitemActiveCallBinding? by bindViewBinding() | |
@SuppressLint("ClickableViewAccessibility") | |
internal fun bindTo(activeCall: ActiveCalls) { | |
bind?.activeCallMoment?.setFastText(convertTimeDateToString(activeCall.moment)) | |
bind?.activeCallStatus?.setFastText(activeCall.status.name) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment