Closed OxygenCobalt closed 2 years ago
You can implement your own [CoordinatorLayout.Behavior](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout.Behavior)
and set it to your content view in the XML.
You should be able to override [layoutDependsOn](https://developer.android.com/reference/androidx/coordinatorlayout/widget/CoordinatorLayout.Behavior#layoutDependsOn(androidx.coordinatorlayout.widget.CoordinatorLayout,%20V,%20android.view.View))
method to make your content view depends on the bottom sheet.
I'm not super familiar with how CoordinatorLayout is working so I suggest you google it. There should be several good articles on the Web. : )
Okay, after some trial and error I was able to create a CoordinatorLayout.Behavior
that does what I want properly. Note that I am relying on an extension value called "offset" that returns the current slide offset of the bottom sheet.
Just putting this here for anyone to use. Probably not the best implementation efficiency-wise, although that's because I threw it together in a few hours.
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.WindowInsets
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.WindowInsetsCompat
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class BottomSheetViewBehavior<V: View>(context: Context, attributeSet: AttributeSet?) :
CoordinatorLayout.Behavior<V>(context, attributeSet) {
private var lastInsets: WindowInsets? = null
private var dep: View? = null
private var setup: Boolean = false
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
if ((dependency.layoutParams as CoordinatorLayout.LayoutParams).behavior is BottomSheetBehavior) {
dep = dependency
return true
}
return false
}
override fun onMeasureChild(
parent: CoordinatorLayout,
child: V,
parentWidthMeasureSpec: Int,
widthUsed: Int,
parentHeightMeasureSpec: Int,
heightUsed: Int
): Boolean {
return measureContent(parent, child, dep ?: return false)
}
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
super.onLayoutChild(parent, child, layoutDirection)
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
if (!setup) {
child.setOnApplyWindowInsetsListener { _, insets ->
lastInsets = insets
val dep = dep ?: return@setOnApplyWindowInsetsListener insets
val bars = insets.systemWindowInsets
val behavior = (dep.layoutParams as CoordinatorLayout.LayoutParams).behavior
as BottomSheetBehavior
if (behavior.peekHeight < 0 || behavior.offset == Float.MIN_VALUE) {
return@setOnApplyWindowInsetsListener insets
}
val adjustedBottomInset = (bars.bottom - behavior.consumedByBar).coerceAtLeast(0)
insets.replaceSystemWindowInsets(
bars.left, bars.top, bars.right, adjustedBottomInset)
}
setup = true
}
return true
}
private fun measureContent(parent: View, child: View, dep: View): Boolean {
val behavior = (dep.layoutParams as CoordinatorLayout.LayoutParams).behavior
as BottomSheetBehavior
if (behavior.peekHeight < 0 || behavior.offset == Float.MIN_VALUE) {
return false
}
val contentWidthSpec = View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.EXACTLY)
val contentHeightSpec =
View.MeasureSpec.makeMeasureSpec(parent.measuredHeight - behavior.consumedByBar, View.MeasureSpec.EXACTLY)
child.measure(contentWidthSpec, contentHeightSpec)
return true
}
private val BottomSheetBehavior<*>.consumedByBar: Int
get() = if (offset >= 0) {
peekHeight
} else {
(peekHeight * (1 - abs(offset))).toInt()
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: V,
dependency: View
): Boolean {
lastInsets?.let(child::dispatchApplyWindowInsets)
return measureContent(parent, child, dependency) &&
onLayoutChild(parent, child, parent.layoutDirection)
}
}
Is your feature request related to a problem? Please describe. My app has a "content" view and a bottom sheet. The content view should adapt it's size and window insets depending on the presence of a bottom sheet. However, when I try to use
BottomSheetBehavior
, the bottom sheet ends up overlapping with the content. This prevents me from leveraging the behavior in my app, and instead forces me to make my own layout implementation.Describe the solution you'd like Something akin to
AppBarLayout.ScrollingViewBehavior
, however with a bottom sheet. When the bottom sheet is hidden, the view with the behavior should resize to take up the whole view and apply window insets. When the bottom sheet is collapsed, the view should resize to take up the portion of the view not already taken up by the collapsed bottom sheet and not apply window insets. If possible, I would also want this to occur as the bottom sheet is sliding, rather than just during a state change.Describe alternatives you've considered I tried to hack together a layout that did my expected behavior, as I cannot wrap my head around the CoordinatorLayout Behavior system.
But apparently the height of some views can be zero at the point where I need to apply window insets, so this whole system just doesn't work at all. It also causes variously scrolling issues, as I'm not resizing the view as I am applying padding to it after the initial layout.