Skip to content

Commit 34b58cc

Browse files
committed
moved snap processing from CollapsingToolbarState to CollapsingToolbarScaffoldState
+ Defaults naming refactoring + TODOs added
1 parent 6a8db15 commit 34b58cc

File tree

5 files changed

+109
-104
lines changed

5 files changed

+109
-104
lines changed

lib/src/main/java/me/onebone/toolbar/AppBarContainer.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ import kotlin.math.max
5252
fun AppbarContainer(
5353
modifier: Modifier = Modifier,
5454
scrollStrategy: ScrollStrategy,
55-
collapsingToolbarState: CollapsingToolbarState,
55+
collapsingToolbarScaffoldState: CollapsingToolbarScaffoldState,
5656
content: @Composable AppbarContainerScope.() -> Unit
5757
) {
5858
AppBarContainer(
5959
modifier = modifier,
6060
scrollStrategy = scrollStrategy,
61-
collapsingToolbarState = collapsingToolbarState,
61+
collapsingToolbarScaffoldState = collapsingToolbarScaffoldState,
6262
content = content
6363
)
6464
}
@@ -75,16 +75,16 @@ fun AppBarContainer(
7575
modifier: Modifier = Modifier,
7676
scrollStrategy: ScrollStrategy,
7777
/** The state of a connected collapsing toolbar */
78-
collapsingToolbarState: CollapsingToolbarState,
78+
collapsingToolbarScaffoldState: CollapsingToolbarScaffoldState,
7979
content: @Composable AppbarContainerScope.() -> Unit
8080
) {
8181
val offsetY = remember { mutableStateOf(0) }
8282
val flingBehavior = ScrollableDefaults.flingBehavior()
8383
val snapStrategy = null
8484

85-
val (scope, measurePolicy) = remember(scrollStrategy, collapsingToolbarState) {
86-
AppbarContainerScopeImpl(scrollStrategy.create(offsetY, collapsingToolbarState, flingBehavior, snapStrategy)) to
87-
AppbarMeasurePolicy(scrollStrategy, collapsingToolbarState, offsetY)
85+
val (scope, measurePolicy) = remember(scrollStrategy, collapsingToolbarScaffoldState) {
86+
AppbarContainerScopeImpl(scrollStrategy.create(offsetY, collapsingToolbarScaffoldState, flingBehavior, snapStrategy)) to
87+
AppbarMeasurePolicy(scrollStrategy, collapsingToolbarScaffoldState.toolbarState, offsetY)
8888
}
8989

9090
Layout(

lib/src/main/java/me/onebone/toolbar/CollapsingToolbar.kt

Lines changed: 6 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,11 @@
2323
package me.onebone.toolbar
2424

2525
import androidx.annotation.FloatRange
26-
import androidx.compose.animation.core.AnimationState
27-
import androidx.compose.animation.core.animateTo
28-
import androidx.compose.animation.core.tween
2926
import androidx.compose.foundation.MutatePriority
3027
import androidx.compose.foundation.gestures.FlingBehavior
3128
import androidx.compose.foundation.gestures.ScrollScope
3229
import androidx.compose.foundation.gestures.ScrollableState
3330
import androidx.compose.runtime.Composable
34-
import androidx.compose.runtime.MutableState
3531
import androidx.compose.runtime.Stable
3632
import androidx.compose.runtime.getValue
3733
import androidx.compose.runtime.mutableStateOf
@@ -58,7 +54,7 @@ import kotlin.math.roundToInt
5854

5955
@Stable
6056
class CollapsingToolbarState(
61-
initial: Int = CollapsingToolbarDefaults.INITIAL_HEIGHT
57+
initial: Int = CollapsingToolbarDefaults.InitialHeight
6258
): ScrollableState {
6359
/**
6460
* [height] indicates current height of the toolbar.
@@ -137,74 +133,6 @@ class CollapsingToolbarState(
137133
)
138134
fun feedScroll(value: Float): Float = dispatchRawDelta(value)
139135

140-
// TODO: A strange jump in snap speed is often observed
141-
@ExperimentalToolbarApi
142-
suspend fun expand(duration: Int = CollapsingToolbarDefaults.EXPAND_DURATION) {
143-
val anim = AnimationState(height.toFloat())
144-
145-
scroll {
146-
var prev = anim.value
147-
anim.animateTo(maxHeight.toFloat(), tween(duration)) {
148-
scrollBy(value - prev)
149-
prev = value
150-
}
151-
}
152-
}
153-
154-
// TODO: A strange jump in snap speed is often observed
155-
@ExperimentalToolbarApi
156-
suspend fun collapse(duration: Int = CollapsingToolbarDefaults.COLLAPSE_DURATION) {
157-
val anim = AnimationState(height.toFloat())
158-
159-
scroll {
160-
var prev = anim.value
161-
anim.animateTo(minHeight.toFloat(), tween(duration)) {
162-
scrollBy(value - prev)
163-
prev = value
164-
}
165-
}
166-
}
167-
168-
@ExperimentalToolbarApi
169-
suspend fun expandOffset(snapStrategy: SnapStrategy, offsetY: MutableState<Int>) {
170-
val anim = AnimationState(offsetY.value.toFloat())
171-
172-
anim.animateTo(0f, tween(snapStrategy.expandDuration)) {
173-
offsetY.value = value.toInt()
174-
}
175-
}
176-
177-
@ExperimentalToolbarApi
178-
suspend fun collapseOffset(snapStrategy: SnapStrategy, offsetY: MutableState<Int>) {
179-
val anim = AnimationState(offsetY.value.toFloat())
180-
181-
anim.animateTo(-minHeight.toFloat(), tween(snapStrategy.collapseDuration)) {
182-
offsetY.value = value.toInt()
183-
}
184-
}
185-
186-
// TODO: Is there a better solution rather OptIn ExperimentalToolbarApi?
187-
@OptIn(ExperimentalToolbarApi::class)
188-
internal suspend fun processSnap(strategy: SnapStrategy) {
189-
if (progress > strategy.edge) {
190-
expand(strategy.expandDuration)
191-
} else {
192-
collapse(strategy.collapseDuration)
193-
}
194-
}
195-
196-
// TODO: Is there a better solution rather OptIn ExperimentalToolbarApi?
197-
@OptIn(ExperimentalToolbarApi::class)
198-
internal suspend fun processOffsetSnap(snapStrategy: SnapStrategy, offsetY: MutableState<Int>) {
199-
val offsetProgress =
200-
1f - ((offsetY.value / (minHeight / 100f)) / 100f).absoluteValue
201-
if (offsetProgress > snapStrategy.edge) {
202-
expandOffset(snapStrategy, offsetY)
203-
} else {
204-
collapseOffset(snapStrategy, offsetY)
205-
}
206-
}
207-
208136
/**
209137
* @return Remaining velocity after fling
210138
*/
@@ -232,7 +160,7 @@ class CollapsingToolbarState(
232160

233161
@Composable
234162
fun rememberCollapsingToolbarState(
235-
initial: Int = CollapsingToolbarDefaults.INITIAL_HEIGHT
163+
initial: Int = CollapsingToolbarDefaults.InitialHeight
236164
): CollapsingToolbarState {
237165
return remember {
238166
CollapsingToolbarState(
@@ -260,10 +188,10 @@ fun CollapsingToolbar(
260188
}
261189

262190
object CollapsingToolbarDefaults {
263-
const val INITIAL_HEIGHT = Int.MAX_VALUE
264-
const val EDGE = 0.5f
265-
const val EXPAND_DURATION = 200
266-
const val COLLAPSE_DURATION = 200
191+
const val InitialHeight = Int.MAX_VALUE
192+
const val Edge = 0.5f
193+
const val ExpandDuration = 200
194+
const val CollapseDuration = 200
267195
}
268196

269197
private class CollapsingToolbarMeasurePolicy(

lib/src/main/java/me/onebone/toolbar/CollapsingToolbarScaffold.kt

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
package me.onebone.toolbar
2424

2525
import android.os.Bundle
26+
import androidx.compose.animation.core.AnimationState
27+
import androidx.compose.animation.core.animateTo
28+
import androidx.compose.animation.core.tween
2629
import androidx.compose.foundation.gestures.ScrollableDefaults
2730
import androidx.compose.runtime.Composable
2831
import androidx.compose.runtime.Stable
@@ -34,6 +37,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
3437
import androidx.compose.ui.Modifier
3538
import androidx.compose.ui.input.nestedscroll.nestedScroll
3639
import androidx.compose.ui.layout.SubcomposeLayout
40+
import kotlin.math.absoluteValue
3741
import kotlin.math.max
3842

3943
@Stable
@@ -45,6 +49,75 @@ class CollapsingToolbarScaffoldState(
4549
get() = offsetYState.value
4650

4751
internal val offsetYState = mutableStateOf(initialOffsetY)
52+
53+
// TODO: A strange jump in snap speed is often observed
54+
@ExperimentalToolbarApi
55+
suspend fun expand(duration: Int = CollapsingToolbarDefaults.ExpandDuration) {
56+
val anim = AnimationState(toolbarState.height.toFloat())
57+
58+
toolbarState.scroll {
59+
var prev = anim.value
60+
anim.animateTo(toolbarState.maxHeight.toFloat(), tween(duration)) {
61+
scrollBy(value - prev)
62+
prev = value
63+
}
64+
}
65+
}
66+
67+
// TODO: A strange jump in snap speed is often observed
68+
@ExperimentalToolbarApi
69+
suspend fun collapse(duration: Int = CollapsingToolbarDefaults.CollapseDuration) {
70+
val anim = AnimationState(toolbarState.height.toFloat())
71+
72+
toolbarState.scroll {
73+
var prev = anim.value
74+
anim.animateTo(toolbarState.minHeight.toFloat(), tween(duration)) {
75+
scrollBy(value - prev)
76+
prev = value
77+
}
78+
}
79+
}
80+
81+
@ExperimentalToolbarApi
82+
suspend fun expandOffset(snapStrategy: SnapStrategy) {
83+
val anim = AnimationState(offsetYState.value.toFloat())
84+
85+
anim.animateTo(0f, tween(snapStrategy.expandDuration)) {
86+
offsetYState.value = value.toInt()
87+
}
88+
}
89+
90+
@ExperimentalToolbarApi
91+
suspend fun collapseOffset(snapStrategy: SnapStrategy) {
92+
val anim = AnimationState(offsetYState.value.toFloat())
93+
94+
anim.animateTo(-toolbarState.minHeight.toFloat(), tween(snapStrategy.collapseDuration)) {
95+
offsetYState.value = value.toInt()
96+
}
97+
}
98+
99+
// TODO: Is there a better solution rather OptIn ExperimentalToolbarApi?
100+
@OptIn(ExperimentalToolbarApi::class)
101+
internal suspend fun processSnap(strategy: SnapStrategy) {
102+
if (toolbarState.progress > strategy.edge) {
103+
expand(strategy.expandDuration)
104+
} else {
105+
collapse(strategy.collapseDuration)
106+
}
107+
}
108+
109+
// TODO: Is there a better solution rather OptIn ExperimentalToolbarApi?
110+
@OptIn(ExperimentalToolbarApi::class)
111+
internal suspend fun processOffsetSnap(snapStrategy: SnapStrategy) {
112+
// TODO: Refactor ugly math
113+
val offsetProgress =
114+
1f - ((offsetYState.value / (toolbarState.minHeight / 100f)) / 100f).absoluteValue
115+
if (offsetProgress > snapStrategy.edge) {
116+
expandOffset(snapStrategy)
117+
} else {
118+
collapseOffset(snapStrategy)
119+
}
120+
}
48121
}
49122

50123
private class CollapsingToolbarScaffoldStateSaver: Saver<CollapsingToolbarScaffoldState, Bundle> {
@@ -83,7 +156,8 @@ fun CollapsingToolbarScaffold(
83156
val flingBehavior = ScrollableDefaults.flingBehavior()
84157

85158
val nestedScrollConnection = remember(scrollStrategy, state) {
86-
scrollStrategy.create(state.offsetYState, state.toolbarState, flingBehavior, snapStrategy)
159+
// TODO by RareScrap: Should we make offsetYState public and pass just state?
160+
scrollStrategy.create(state.offsetYState, state, flingBehavior, snapStrategy)
87161
}
88162

89163
val toolbarState = state.toolbarState

lib/src/main/java/me/onebone/toolbar/ScrollStrategy.kt

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,34 +33,34 @@ enum class ScrollStrategy {
3333
EnterAlways {
3434
override fun create(
3535
offsetY: MutableState<Int>,
36-
toolbarState: CollapsingToolbarState,
36+
scaffoldState: CollapsingToolbarScaffoldState,
3737
flingBehavior: FlingBehavior,
3838
snapStrategy: SnapStrategy?,
3939
): NestedScrollConnection =
40-
EnterAlwaysNestedScrollConnection(offsetY, toolbarState, flingBehavior, snapStrategy)
40+
EnterAlwaysNestedScrollConnection(offsetY, scaffoldState, flingBehavior, snapStrategy)
4141
},
4242
EnterAlwaysCollapsed {
4343
override fun create(
4444
offsetY: MutableState<Int>,
45-
toolbarState: CollapsingToolbarState,
45+
scaffoldState: CollapsingToolbarScaffoldState,
4646
flingBehavior: FlingBehavior,
4747
snapStrategy: SnapStrategy?,
4848
): NestedScrollConnection =
49-
EnterAlwaysCollapsedNestedScrollConnection(offsetY, toolbarState, flingBehavior, snapStrategy)
49+
EnterAlwaysCollapsedNestedScrollConnection(offsetY, scaffoldState, flingBehavior, snapStrategy)
5050
},
5151
ExitUntilCollapsed {
5252
override fun create(
5353
offsetY: MutableState<Int>,
54-
toolbarState: CollapsingToolbarState,
54+
scaffoldState: CollapsingToolbarScaffoldState,
5555
flingBehavior: FlingBehavior,
5656
snapStrategy: SnapStrategy?,
5757
): NestedScrollConnection =
58-
ExitUntilCollapsedNestedScrollConnection(toolbarState, flingBehavior, snapStrategy)
58+
ExitUntilCollapsedNestedScrollConnection(scaffoldState, flingBehavior, snapStrategy)
5959
};
6060

6161
internal abstract fun create(
6262
offsetY: MutableState<Int>,
63-
toolbarState: CollapsingToolbarState,
63+
scaffoldState: CollapsingToolbarScaffoldState,
6464
flingBehavior: FlingBehavior,
6565
snapStrategy: SnapStrategy?,
6666
): NestedScrollConnection
@@ -83,12 +83,13 @@ private class ScrollDelegate(
8383

8484
internal class EnterAlwaysNestedScrollConnection(
8585
private val offsetY: MutableState<Int>,
86-
private val toolbarState: CollapsingToolbarState,
86+
private val scaffoldState: CollapsingToolbarScaffoldState,
8787
private val flingBehavior: FlingBehavior,
8888
private val snapStrategy: SnapStrategy?
8989
): NestedScrollConnection {
9090
private val scrollDelegate = ScrollDelegate(offsetY)
9191
private val tracker = RelativeVelocityTracker(CurrentTimeProviderImpl())
92+
private val toolbarState = scaffoldState.toolbarState
9293

9394
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
9495
val dy = available.y
@@ -140,9 +141,9 @@ internal class EnterAlwaysNestedScrollConnection(
140141
// When the toolbar is hiding, it does it through changing the offset and does not
141142
// change its height, so we must process not the snap of the toolbar, but the
142143
// snap of its offset.
143-
toolbarState.processOffsetSnap(it, offsetY)
144+
scaffoldState.processOffsetSnap(it)
144145
} else {
145-
toolbarState.processSnap(it)
146+
scaffoldState.processSnap(it)
146147
}
147148
}
148149

@@ -152,12 +153,13 @@ internal class EnterAlwaysNestedScrollConnection(
152153

153154
internal class EnterAlwaysCollapsedNestedScrollConnection(
154155
private val offsetY: MutableState<Int>,
155-
private val toolbarState: CollapsingToolbarState,
156+
private val scaffoldState: CollapsingToolbarScaffoldState,
156157
private val flingBehavior: FlingBehavior,
157158
private val snapStrategy: SnapStrategy?,
158159
): NestedScrollConnection {
159160
private val scrollDelegate = ScrollDelegate(offsetY)
160161
private val tracker = RelativeVelocityTracker(CurrentTimeProviderImpl())
162+
private val toolbarState = scaffoldState.toolbarState
161163

162164
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
163165
val dy = available.y
@@ -211,14 +213,14 @@ internal class EnterAlwaysCollapsedNestedScrollConnection(
211213

212214
// TODO: Cancel expand/collapse animation inside onPreScroll
213215
snapStrategy?.let {
214-
val isToolbarChangingOffset = offsetY.value != 0//toolbarState.progress == 0f
216+
val isToolbarChangingOffset = offsetY.value != 0
215217
if (isToolbarChangingOffset) {
216218
// When the toolbar is hiding, it does it through changing the offset and does not
217219
// change its height, so we must process not the snap of the toolbar, but the
218220
// snap of its offset.
219-
toolbarState.processOffsetSnap(it, offsetY)
221+
scaffoldState.processOffsetSnap(it)
220222
} else {
221-
toolbarState.processSnap(it)
223+
scaffoldState.processSnap(it)
222224
}
223225
}
224226

@@ -227,11 +229,12 @@ internal class EnterAlwaysCollapsedNestedScrollConnection(
227229
}
228230

229231
internal class ExitUntilCollapsedNestedScrollConnection(
230-
private val toolbarState: CollapsingToolbarState,
232+
private val scaffoldState: CollapsingToolbarScaffoldState,
231233
private val flingBehavior: FlingBehavior,
232234
private val snapStrategy: SnapStrategy?
233235
): NestedScrollConnection {
234236
private val tracker = RelativeVelocityTracker(CurrentTimeProviderImpl())
237+
private val toolbarState = scaffoldState.toolbarState
235238

236239
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
237240
val dy = available.y
@@ -284,7 +287,7 @@ internal class ExitUntilCollapsedNestedScrollConnection(
284287
}
285288

286289
// TODO: Cancel expand/collapse animation inside onPreScroll
287-
snapStrategy?.let { toolbarState.processSnap(it) }
290+
snapStrategy?.let { scaffoldState.processSnap(it) }
288291

289292
return available.copy(y = available.y - left)
290293
}

0 commit comments

Comments
 (0)