Skip to content

Instantly share code, notes, and snippets.

@petedoyle
Created June 15, 2019 02:22
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 petedoyle/9b583fea3b480516b1b440d1b52267de to your computer and use it in GitHub Desktop.
Save petedoyle/9b583fea3b480516b1b440d1b52267de to your computer and use it in GitHub Desktop.
Utilities for working with System Window Insets, based on Chris Banes' blog post: https://medium.com/androiddevelopers/windowinsets-listeners-to-layouts-8f9ccc8fa4d1
package com.amazon.phseven.common.app
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import androidx.core.view.updateLayoutParams
import androidx.databinding.BindingAdapter
import androidx.fragment.app.Fragment
/**
* Ask the system to call [View.onApplyWindowInsets] with insets. Unlike
* [View.requestApplyInsets], this is safe to call before a view is
* attached to a window, for example: from [Fragment.onCreateView].
*
* Sourced from Chris Banes' post here:
* https://medium.com/androiddevelopers/windowinsets-listeners-to-layouts-8f9ccc8fa4d1
*/
fun View.requestApplyInsetsWhenAttached() {
when (isAttachedToWindow) {
true -> requestApplyInsets() // View is attached to window, just request as normal
else -> { // Wait until view is attached to window, then request
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
v.removeOnAttachStateChangeListener(this)
v.requestApplyInsets()
}
override fun onViewDetachedFromWindow(v: View) = Unit
})
}
}
}
fun View.doOnApplyWindowInsets(function: (View, WindowInsets, InitialPadding, InitialMargin) -> Unit) {
val initialPadding = recordInitialPaddingForView(this) // Remember the intrinsic padding from XML
val initialMargin = recordInitialMarginForView(this) // Remember the intrinsic margin from XML
// Set an actual OnApplyWindowInsetsListener which proxies to the given
// lambda, also passing in the original padding state
setOnApplyWindowInsetsListener { view, insets ->
function(view, insets, initialPadding, initialMargin)
insets // Always return the insets, so that children can also use them
}
requestApplyInsetsWhenAttached() // request some insets
}
data class InitialPadding(
val left: Int,
val top: Int,
val right: Int,
val bottom: Int
)
data class InitialMargin(
val left: Int,
val top: Int,
val right: Int,
val bottom: Int
)
private fun recordInitialPaddingForView(view: View) = InitialPadding(
view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom
)
private fun recordInitialMarginForView(view: View): InitialMargin {
val lp = view.layoutParams
return when (lp is ViewGroup.MarginLayoutParams) {
true -> InitialMargin(lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin)
else -> InitialMargin(0, 0, 0, 0)
}
}
@BindingAdapter(
"paddingLeftSystemWindowInsets",
"paddingTopSystemWindowInsets",
"paddingRightSystemWindowInsets",
"paddingBottomSystemWindowInsets",
"marginLeftSystemWindowInsets",
"marginTopSystemWindowInsets",
"marginRightSystemWindowInsets",
"marginBottomSystemWindowInsets",
requireAll = false
)
fun View.applySystemWindows(
applyPaddingLeft: Boolean = false,
applyPaddingTop: Boolean = false,
applyPaddingRight: Boolean = false,
applyPaddingBottom: Boolean = false,
applyMarginLeft: Boolean = false,
applyMarginTop: Boolean = false,
applyMarginRight: Boolean = false,
applyMarginBottom: Boolean = false
) {
doOnApplyWindowInsets { view, insets, initialPadding, initialMargin ->
val paddingLeft = if (applyPaddingLeft) insets.systemWindowInsetLeft else 0
val paddingTop = if (applyPaddingTop) insets.systemWindowInsetTop else 0
val paddingRight = if (applyPaddingRight) insets.systemWindowInsetRight else 0
val paddingBottom = if (applyPaddingBottom) insets.systemWindowInsetBottom else 0
val marginLeft = if (applyMarginLeft) insets.systemWindowInsetLeft else 0
val marginTop = if (applyMarginTop) insets.systemWindowInsetTop else 0
val marginRight = if (applyMarginRight) insets.systemWindowInsetRight else 0
val marginBottom = if (applyMarginBottom) insets.systemWindowInsetBottom else 0
view.setPadding(
initialPadding.left + paddingLeft,
initialPadding.top + paddingTop,
initialPadding.right + paddingRight,
initialPadding.bottom + paddingBottom
)
if (layoutParams is ViewGroup.MarginLayoutParams) {
updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = initialMargin.left + marginLeft
topMargin = initialMargin.top + marginTop
rightMargin = initialMargin.right + marginRight
bottomMargin = initialMargin.bottom + marginBottom
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment