17
17
package com.materialstudies.owl.ui.onboarding
18
18
19
19
import android.os.Bundle
20
+ import android.util.DisplayMetrics
20
21
import android.view.LayoutInflater
21
22
import android.view.View
22
23
import android.view.ViewGroup
23
24
import androidx.annotation.Px
24
25
import androidx.core.view.forEach
25
- import androidx.core.view.postDelayed
26
26
import androidx.dynamicanimation.animation.SpringAnimation
27
27
import androidx.fragment.app.Fragment
28
28
import androidx.navigation.fragment.findNavController
29
+ import androidx.recyclerview.widget.LinearSmoothScroller
29
30
import androidx.recyclerview.widget.RecyclerView
30
31
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING
31
32
import com.materialstudies.owl.R
@@ -53,7 +54,7 @@ class OnboardingFragment : Fragment() {
53
54
// data ordered LTR, so reverse it before setting
54
55
submitList(topics.reversed())
55
56
}
56
- smoothScrollToPosition (topics.size)
57
+ smoothScrollToPositionWithSpeed (topics.size)
57
58
addOnScrollListener(
58
59
OscillatingScrollListener (resources.getDimensionPixelSize(R .dimen.grid_2))
59
60
)
@@ -67,14 +68,18 @@ class OnboardingFragment : Fragment() {
67
68
* Oscillates a [RecyclerView]'s children based on the horizontal scroll velocity.
68
69
*/
69
70
private const val MAX_OSCILLATION_ANGLE = 6f // ±6º
71
+
70
72
class OscillatingScrollListener (
71
73
@Px private val scrollDistance : Int
72
74
) : RecyclerView.OnScrollListener() {
73
75
override fun onScrolled (recyclerView : RecyclerView , dx : Int , dy : Int ) {
74
76
// Calculate a rotation to set from the horizontal scroll
75
- val rotation =
76
- (dx.coerceIn( - scrollDistance, scrollDistance) / scrollDistance) * MAX_OSCILLATION_ANGLE
77
+ val clampedDx = dx.coerceIn( - scrollDistance, scrollDistance)
78
+ val rotation = (clampedDx / scrollDistance) * MAX_OSCILLATION_ANGLE
77
79
recyclerView.forEach {
80
+ // Alter the pivot point based on scroll direction to make motion look more natural
81
+ it.pivotX = it.width / 2f + clampedDx / 3f
82
+ it.pivotY = it.height / 3f
78
83
it.spring(SpringAnimation .ROTATION ).animateToFinalPosition(rotation)
79
84
}
80
85
}
@@ -87,3 +92,17 @@ class OscillatingScrollListener(
87
92
}
88
93
}
89
94
}
95
+
96
+ fun RecyclerView.smoothScrollToPositionWithSpeed (
97
+ position : Int ,
98
+ millisPerInch : Float = 100f
99
+ ) {
100
+ val lm = requireNotNull(layoutManager)
101
+ val scroller = object : LinearSmoothScroller (context) {
102
+ override fun calculateSpeedPerPixel (displayMetrics : DisplayMetrics ): Float {
103
+ return millisPerInch / displayMetrics.densityDpi
104
+ }
105
+ }
106
+ scroller.targetPosition = position
107
+ lm.startSmoothScroll(scroller)
108
+ }
0 commit comments