Skip to content

Instantly share code, notes, and snippets.

@slavonnet
Last active December 28, 2021 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slavonnet/4fca3c62f8d50d8dadd1687426415de0 to your computer and use it in GitHub Desktop.
Save slavonnet/4fca3c62f8d50d8dadd1687426415de0 to your computer and use it in GitHub Desktop.
Delegate for auto detect ViewBinding and Binding
// 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