Skip to content

Instantly share code, notes, and snippets.

@Bloody-Badboy
Last active July 9, 2022 21:13
Show Gist options
  • Save Bloody-Badboy/277827cf329f25aa3d6cae89cc327680 to your computer and use it in GitHub Desktop.
Save Bloody-Badboy/277827cf329f25aa3d6cae89cc327680 to your computer and use it in GitHub Desktop.
SystemWindowInsets Extensions + Binding Adapters
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