ui: switch to inset-based bottom sheet content
Do not do a measure + inset method with BottomSheetContentBehavior, instead, try to re-apply window insets to adapt with the bar instead. This fixes a lot of view clipping issues that made motion transitions non-ideal and prevented a rounded playback bar. Only remaining issue is RecyclerView instances, which need to be further reworked to resolve scroll issues and edge effect problems.
This commit is contained in:
parent
913db88fde
commit
0474940ee3
7 changed files with 35 additions and 47 deletions
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
## dev
|
## dev
|
||||||
|
|
||||||
|
#### What's Improved
|
||||||
|
- Improved bottom sheet code to prevent clipping issues
|
||||||
|
|
||||||
|
#### What's Fixed
|
||||||
|
- Fixed incorrect font being used in the queue title
|
||||||
|
|
||||||
## 2.6.0
|
## 2.6.0
|
||||||
|
|
||||||
#### What's New
|
#### What's New
|
||||||
|
|
|
@ -22,7 +22,6 @@ import android.util.AttributeSet
|
||||||
import android.view.WindowInsets
|
import android.view.WindowInsets
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.annotation.AttrRes
|
import androidx.annotation.AttrRes
|
||||||
import androidx.core.view.updatePadding
|
|
||||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,12 +36,9 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
|
||||||
clipToPadding = false
|
clipToPadding = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {
|
|
||||||
return onApplyWindowInsets(insets)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
|
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
|
||||||
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
|
// Save a layout by simply moving the view bounds upwards
|
||||||
|
translationY = -insets.systemBarInsetsCompat.bottom.toFloat()
|
||||||
return insets
|
return insets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,10 @@ import android.util.AttributeSet
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.ui.AuxioSheetBehavior
|
import org.oxycblt.auxio.ui.AuxioSheetBehavior
|
||||||
|
import org.oxycblt.auxio.util.getDimen
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The coordinator layout behavior used for the playback sheet, hacking in the many fixes required
|
* The coordinator layout behavior used for the playback sheet, hacking in the many fixes required
|
||||||
|
@ -33,6 +36,9 @@ class PlaybackSheetBehavior<V : View>(context: Context, attributeSet: AttributeS
|
||||||
AuxioSheetBehavior<V>(context, attributeSet) {
|
AuxioSheetBehavior<V>(context, attributeSet) {
|
||||||
init {
|
init {
|
||||||
isHideable = true
|
isHideable = true
|
||||||
|
if (Settings(context).roundMode) {
|
||||||
|
sheetBackgroundDrawable.setCornerSize(context.getDimen(R.dimen.size_corners_medium))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack around issue where the playback sheet will try to intercept nested scrolling events
|
// Hack around issue where the playback sheet will try to intercept nested scrolling events
|
||||||
|
|
|
@ -130,7 +130,7 @@ class QueueDragCallback(private val playbackModel: QueueViewModel) : ItemTouchHe
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
target: RecyclerView.ViewHolder
|
target: RecyclerView.ViewHolder
|
||||||
): Boolean =
|
) =
|
||||||
playbackModel.moveQueueDataItems(
|
playbackModel.moveQueueDataItems(
|
||||||
viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package org.oxycblt.auxio.ui
|
package org.oxycblt.auxio.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.graphics.drawable.LayerDrawable
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
@ -73,12 +72,12 @@ abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: Attr
|
||||||
child.apply {
|
child.apply {
|
||||||
// Sometimes the sheet background will fade out, so guard it with another
|
// Sometimes the sheet background will fade out, so guard it with another
|
||||||
// colorSurface drawable to prevent content overlap.
|
// colorSurface drawable to prevent content overlap.
|
||||||
background =
|
val guardDrawable =
|
||||||
LayerDrawable(
|
MaterialShapeDrawable(sheetBackgroundDrawable.shapeAppearanceModel).apply {
|
||||||
arrayOf(
|
fillColor = context.getAttrColorCompat(R.attr.colorSurface)
|
||||||
ColorDrawable(
|
}
|
||||||
context.getAttrColorCompat(R.attr.colorSurface).defaultColor),
|
|
||||||
sheetBackgroundDrawable))
|
background = LayerDrawable(arrayOf(guardDrawable, sheetBackgroundDrawable))
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
// Shadows aren't disabled by default, do that.
|
// Shadows aren't disabled by default, do that.
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.view.WindowInsets
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
import com.google.android.material.bottomsheet.NeoBottomSheetBehavior
|
import com.google.android.material.bottomsheet.NeoBottomSheetBehavior
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
import org.oxycblt.auxio.util.coordinatorLayoutBehavior
|
import org.oxycblt.auxio.util.coordinatorLayoutBehavior
|
||||||
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
|
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
|
||||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||||
|
@ -62,15 +63,7 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
|
||||||
|
|
||||||
if (consumed != lastConsumed) {
|
if (consumed != lastConsumed) {
|
||||||
lastConsumed = consumed
|
lastConsumed = consumed
|
||||||
|
|
||||||
val insets = lastInsets
|
|
||||||
if (insets != null) {
|
|
||||||
child.dispatchApplyWindowInsets(insets)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastInsets?.let(child::dispatchApplyWindowInsets)
|
lastInsets?.let(child::dispatchApplyWindowInsets)
|
||||||
measureContent(parent, child, consumed)
|
|
||||||
layoutContent(child)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,21 +78,19 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
|
||||||
parentHeightMeasureSpec: Int,
|
parentHeightMeasureSpec: Int,
|
||||||
heightUsed: Int
|
heightUsed: Int
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val dep = dep ?: return false
|
val contentWidthSpec =
|
||||||
val behavior = dep.coordinatorLayoutBehavior as NeoBottomSheetBehavior
|
View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.EXACTLY)
|
||||||
val consumed = behavior.calculateConsumedByBar()
|
val contentHeightSpec =
|
||||||
if (consumed == Int.MIN_VALUE) {
|
View.MeasureSpec.makeMeasureSpec(parent.measuredHeight, View.MeasureSpec.EXACTLY)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
measureContent(parent, child, consumed)
|
child.measure(contentWidthSpec, contentHeightSpec)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
|
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
|
||||||
super.onLayoutChild(parent, child, layoutDirection)
|
super.onLayoutChild(parent, child, layoutDirection)
|
||||||
layoutContent(child)
|
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
|
||||||
|
|
||||||
if (!setup) {
|
if (!setup) {
|
||||||
child.setOnApplyWindowInsetsListener { v, insets ->
|
child.setOnApplyWindowInsetsListener { v, insets ->
|
||||||
|
@ -112,9 +103,9 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
|
||||||
}
|
}
|
||||||
|
|
||||||
val bars = insets.systemBarInsetsCompat
|
val bars = insets.systemBarInsetsCompat
|
||||||
|
val newBottom = max(bars.bottom, consumed)
|
||||||
|
|
||||||
insets.replaceSystemBarInsetsCompat(
|
insets.replaceSystemBarInsetsCompat(bars.left, bars.top, bars.right, newBottom)
|
||||||
bars.left, bars.top, bars.right, (bars.bottom - consumed).coerceAtLeast(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setup = true
|
setup = true
|
||||||
|
@ -123,20 +114,6 @@ class BottomSheetContentBehavior<V : View>(context: Context, attributeSet: Attri
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun measureContent(parent: View, child: View, consumed: Int) {
|
|
||||||
val contentWidthSpec =
|
|
||||||
View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.EXACTLY)
|
|
||||||
val contentHeightSpec =
|
|
||||||
View.MeasureSpec.makeMeasureSpec(
|
|
||||||
parent.measuredHeight - consumed, View.MeasureSpec.EXACTLY)
|
|
||||||
|
|
||||||
child.measure(contentWidthSpec, contentHeightSpec)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun layoutContent(child: View) {
|
|
||||||
child.layout(0, 0, child.measuredWidth, child.measuredHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun NeoBottomSheetBehavior<*>.calculateConsumedByBar(): Int {
|
private fun NeoBottomSheetBehavior<*>.calculateConsumedByBar(): Int {
|
||||||
val offset = calculateSlideOffset()
|
val offset = calculateSlideOffset()
|
||||||
if (offset == Float.MIN_VALUE || peekHeight < 0) {
|
if (offset == Float.MIN_VALUE || peekHeight < 0) {
|
||||||
|
|
|
@ -27,7 +27,11 @@ import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||||
|
|
||||||
/** A [RecyclerView] that automatically applies insets to itself. */
|
/**
|
||||||
|
* A [RecyclerView] that automatically applies insets to itself.
|
||||||
|
*
|
||||||
|
* TODO: Correctly handle edge-to-edge regarding scroll effects and saved scroll positions.
|
||||||
|
*/
|
||||||
open class AuxioRecyclerView
|
open class AuxioRecyclerView
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) :
|
||||||
|
|
Loading…
Reference in a new issue