Skip to content

Commit 6a8db15

Browse files
committed
appbar snap implementation
1 parent ab42e1b commit 6a8db15

File tree

3 files changed

+74
-11
lines changed

3 files changed

+74
-11
lines changed

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import androidx.compose.foundation.gestures.FlingBehavior
3131
import androidx.compose.foundation.gestures.ScrollScope
3232
import androidx.compose.foundation.gestures.ScrollableState
3333
import androidx.compose.runtime.Composable
34+
import androidx.compose.runtime.MutableState
3435
import androidx.compose.runtime.Stable
3536
import androidx.compose.runtime.getValue
3637
import androidx.compose.runtime.mutableStateOf
@@ -136,6 +137,7 @@ class CollapsingToolbarState(
136137
)
137138
fun feedScroll(value: Float): Float = dispatchRawDelta(value)
138139

140+
// TODO: A strange jump in snap speed is often observed
139141
@ExperimentalToolbarApi
140142
suspend fun expand(duration: Int = CollapsingToolbarDefaults.EXPAND_DURATION) {
141143
val anim = AnimationState(height.toFloat())
@@ -149,6 +151,7 @@ class CollapsingToolbarState(
149151
}
150152
}
151153

154+
// TODO: A strange jump in snap speed is often observed
152155
@ExperimentalToolbarApi
153156
suspend fun collapse(duration: Int = CollapsingToolbarDefaults.COLLAPSE_DURATION) {
154157
val anim = AnimationState(height.toFloat())
@@ -162,6 +165,46 @@ class CollapsingToolbarState(
162165
}
163166
}
164167

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+
165208
/**
166209
* @return Remaining velocity after fling
167210
*/

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,23 @@ internal class EnterAlwaysNestedScrollConnection(
131131

132132
return available.copy(y = available.y - left)
133133
}
134+
135+
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
136+
// TODO: Cancel expand/collapse animation inside onPreScroll
137+
snapStrategy?.let {
138+
val isToolbarChangingOffset = offsetY.value != 0
139+
if (isToolbarChangingOffset) {
140+
// When the toolbar is hiding, it does it through changing the offset and does not
141+
// change its height, so we must process not the snap of the toolbar, but the
142+
// snap of its offset.
143+
toolbarState.processOffsetSnap(it, offsetY)
144+
} else {
145+
toolbarState.processSnap(it)
146+
}
147+
}
148+
149+
return super.onPostFling(consumed, available)
150+
}
134151
}
135152

136153
internal class EnterAlwaysCollapsedNestedScrollConnection(
@@ -192,6 +209,19 @@ internal class EnterAlwaysCollapsedNestedScrollConnection(
192209
dy
193210
}
194211

212+
// TODO: Cancel expand/collapse animation inside onPreScroll
213+
snapStrategy?.let {
214+
val isToolbarChangingOffset = offsetY.value != 0//toolbarState.progress == 0f
215+
if (isToolbarChangingOffset) {
216+
// When the toolbar is hiding, it does it through changing the offset and does not
217+
// change its height, so we must process not the snap of the toolbar, but the
218+
// snap of its offset.
219+
toolbarState.processOffsetSnap(it, offsetY)
220+
} else {
221+
toolbarState.processSnap(it)
222+
}
223+
}
224+
195225
return available.copy(y = available.y - left)
196226
}
197227
}

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,4 @@ class SnapStrategy(
88
@FloatRange(from = 0.0, to = 1.0) val edge: Float = CollapsingToolbarDefaults.EDGE,
99
val expandDuration: Int = CollapsingToolbarDefaults.EXPAND_DURATION,
1010
val collapseDuration: Int = CollapsingToolbarDefaults.COLLAPSE_DURATION
11-
)
12-
13-
// TODO: Is there a better solution rather OptIn ExperimentalToolbarApi?
14-
@OptIn(ExperimentalToolbarApi::class)
15-
internal suspend fun CollapsingToolbarState.processSnap(strategy: SnapStrategy) {
16-
if (progress > strategy.edge) {
17-
expand(strategy.expandDuration)
18-
} else {
19-
collapse(strategy.collapseDuration)
20-
}
21-
}
11+
)

0 commit comments

Comments
 (0)