Last active
July 9, 2022 21:13
-
-
Save Bloody-Badboy/277827cf329f25aa3d6cae89cc327680 to your computer and use it in GitHub Desktop.
SystemWindowInsets Extensions + Binding Adapters
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
package dev.arpan.utils | |
import android.view.View | |
import android.view.ViewGroup | |
import androidx.core.view.ViewCompat | |
import androidx.core.view.WindowInsetsCompat | |
// Make the content ViewGroup ignore insets so that it does not use the default padding | |
@BindingAdapter("ignoreInsets") | |
fun View.ignoreInsets( | |
previousIgnoreInsets: Boolean, | |
ignoreInsets: Boolean | |
) { | |
if (previousIgnoreInsets == ignoreInsets) return | |
setOnApplyWindowInsetsListener { _, insets -> insets } | |
requestApplyInsetsWhenAttached() | |
} | |
@BindingAdapter( | |
"paddingLeftSystemWindowInsets", | |
"paddingTopSystemWindowInsets", | |
"paddingRightSystemWindowInsets", | |
"paddingBottomSystemWindowInsets", | |
requireAll = false | |
) | |
fun View.applySystemWindowInsetsPadding( | |
previousApplyLeft: Boolean, | |
previousApplyTop: Boolean, | |
previousApplyRight: Boolean, | |
previousApplyBottom: Boolean, | |
applyLeft: Boolean, | |
applyTop: Boolean, | |
applyRight: Boolean, | |
applyBottom: Boolean | |
) { | |
if (previousApplyLeft == applyLeft && | |
previousApplyTop == applyTop && | |
previousApplyRight == applyRight && | |
previousApplyBottom == applyBottom | |
) { | |
return | |
} | |
doOnApplyWindowInsets { view, insets, padding, _ -> | |
val left = if (applyLeft) insets.systemWindowInsetLeft else 0 | |
val top = if (applyTop) insets.systemWindowInsetTop else 0 | |
val right = if (applyRight) insets.systemWindowInsetRight else 0 | |
val bottom = if (applyBottom) insets.systemWindowInsetBottom else 0 | |
view.setPadding( | |
padding.left + left, | |
padding.top + top, | |
padding.right + right, | |
padding.bottom + bottom | |
) | |
} | |
} | |
@BindingAdapter( | |
"marginLeftSystemWindowInsets", | |
"marginTopSystemWindowInsets", | |
"marginRightSystemWindowInsets", | |
"marginBottomSystemWindowInsets", | |
requireAll = false | |
) | |
fun View.applySystemWindowInsetsMargin( | |
previousApplyLeft: Boolean, | |
previousApplyTop: Boolean, | |
previousApplyRight: Boolean, | |
previousApplyBottom: Boolean, | |
applyLeft: Boolean, | |
applyTop: Boolean, | |
applyRight: Boolean, | |
applyBottom: Boolean | |
) { | |
if (previousApplyLeft == applyLeft && | |
previousApplyTop == applyTop && | |
previousApplyRight == applyRight && | |
previousApplyBottom == applyBottom | |
) { | |
return | |
} | |
doOnApplyWindowInsets { view, insets, _, margin -> | |
val left = if (applyLeft) insets.systemWindowInsetLeft else 0 | |
val top = if (applyTop) insets.systemWindowInsetTop else 0 | |
val right = if (applyRight) insets.systemWindowInsetRight else 0 | |
val bottom = if (applyBottom) insets.systemWindowInsetBottom else 0 | |
if (view.layoutParams !is ViewGroup.MarginLayoutParams) { | |
throw IllegalArgumentException("Invalid view layout params") | |
} | |
view.updateLayoutParams<ViewGroup.MarginLayoutParams> { | |
leftMargin = margin.left + left | |
topMargin = margin.top + top | |
rightMargin = margin.right + right | |
bottomMargin = margin.bottom + bottom | |
} | |
} | |
} | |
fun View.doOnApplyWindowInsets(f: (View, WindowInsetsCompat, ViewPaddingState, ViewMarginState) -> Unit) { | |
val paddingState = createPaddingStateForView(this) | |
val marginState = createMarginStateForView(this) | |
ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> | |
f(v, insets, paddingState, marginState) | |
insets | |
} | |
requestApplyInsetsWhenAttached() | |
} | |
/** | |
* Call [View.requestApplyInsets] in a safe away. If we're attached it calls it straight-away. | |
* If not it sets an [View.OnAttachStateChangeListener] and waits to be attached before calling | |
* [View.requestApplyInsets]. | |
*/ | |
fun View.requestApplyInsetsWhenAttached() { | |
if (ViewCompat.isAttachedToWindow(this)) { | |
ViewCompat.requestApplyInsets(this) | |
} else { | |
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { | |
override fun onViewAttachedToWindow(v: View) { | |
v.removeOnAttachStateChangeListener(this) | |
ViewCompat.requestApplyInsets(v) | |
} | |
override fun onViewDetachedFromWindow(v: View) = Unit | |
}) | |
} | |
} | |
private fun createPaddingStateForView(view: View) = ViewPaddingState( | |
view.paddingLeft, | |
view.paddingTop, | |
view.paddingRight, | |
view.paddingBottom, | |
view.paddingStart, | |
view.paddingEnd | |
) | |
private fun createMarginStateForView(view: View): ViewMarginState { | |
(view.layoutParams as? ViewGroup.MarginLayoutParams)?.let { | |
return ViewMarginState( | |
it.leftMargin, | |
it.topMargin, | |
it.rightMargin, | |
it.bottomMargin, | |
it.marginStart, | |
it.marginEnd | |
) | |
} | |
return ViewMarginState() | |
} | |
data class ViewPaddingState( | |
val left: Int = 0, | |
val top: Int = 0, | |
val right: Int = 0, | |
val bottom: Int = 0, | |
val start: Int = 0, | |
val end: Int = 0 | |
) | |
data class ViewMarginState( | |
val left: Int = 0, | |
val top: Int = 0, | |
val right: Int = 0, | |
val bottom: Int = 0, | |
val start: Int = 0, | |
val end: Int = 0 | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment