@@ -17,33 +17,27 @@ package app.cash.redwood.lazylayout.compose
17
17
18
18
import androidx.compose.runtime.Composable
19
19
import androidx.compose.runtime.getValue
20
- import androidx.compose.runtime.mutableIntStateOf
21
20
import androidx.compose.runtime.mutableStateOf
22
21
import androidx.compose.runtime.saveable.Saver
23
22
import androidx.compose.runtime.saveable.rememberSaveable
24
23
import androidx.compose.runtime.setValue
25
24
import app.cash.redwood.lazylayout.api.ScrollItemIndex
26
25
27
- private const val DEFAULT_PRELOAD_ITEM_COUNT = 15
28
- private const val SCROLL_IN_PROGRESS_PRELOAD_ITEM_COUNT = 5
29
- private const val PRIMARY_PRELOAD_ITEM_COUNT = 20
30
- private const val SECONDARY_PRELOAD_ITEM_COUNT = 10
31
-
32
- private const val DEFAULT_SCROLL_INDEX = - 1
33
-
34
26
/* *
35
27
* Creates a [LazyListState] that is remembered across compositions.
36
28
*/
37
29
@Composable
38
- public fun rememberLazyListState (): LazyListState {
30
+ public fun rememberLazyListState (
31
+ strategy : LoadingStrategy = ScrollOptimizedLoadingStrategy (),
32
+ ): LazyListState {
39
33
return rememberSaveable(saver = saver) {
40
- LazyListState ()
34
+ LazyListState (strategy )
41
35
}
42
36
}
43
37
44
38
/* * The default [Saver] implementation for [LazyListState]. */
45
39
private val saver: Saver <LazyListState , * > = Saver (
46
- save = { it.firstIndex },
40
+ save = { it.strategy. firstIndex },
47
41
restore = {
48
42
LazyListState ().apply {
49
43
programmaticScroll(firstIndex = it, animated = false , clobberUserScroll = false )
@@ -56,7 +50,9 @@ private val saver: Saver<LazyListState, *> = Saver(
56
50
*
57
51
* In most cases, this will be created via [rememberLazyListState].
58
52
*/
59
- public open class LazyListState {
53
+ public open class LazyListState (
54
+ public val strategy : LoadingStrategy = ScrollOptimizedLoadingStrategy (),
55
+ ) {
60
56
/* *
61
57
* Update this to trigger a programmatic scroll. This may be updated multiple times, including
62
58
* when the previous scroll state is restored.
@@ -69,26 +65,6 @@ public open class LazyListState {
69
65
/* * Once we receive a user scroll, we limit which programmatic scrolls we apply. */
70
66
private var userScrolled = false
71
67
72
- /* * Bounds of what the user is looking at. Everything else is placeholders! */
73
- public var firstIndex: Int by mutableIntStateOf(0 )
74
- private set
75
- public var lastIndex: Int by mutableIntStateOf(0 )
76
- private set
77
-
78
- internal var preloadItems: Boolean = true
79
-
80
- public var defaultPreloadItemCount: Int = DEFAULT_PRELOAD_ITEM_COUNT
81
- public var scrollInProgressPreloadItemCount: Int = SCROLL_IN_PROGRESS_PRELOAD_ITEM_COUNT
82
- public var primaryPreloadItemCount: Int = PRIMARY_PRELOAD_ITEM_COUNT
83
- public var secondaryPreloadItemCount: Int = SECONDARY_PRELOAD_ITEM_COUNT
84
-
85
- private var firstIndexFromPrevious1: Int by mutableIntStateOf(DEFAULT_SCROLL_INDEX )
86
- private var firstIndexFromPrevious2: Int by mutableIntStateOf(DEFAULT_SCROLL_INDEX )
87
- private var lastIndexFromPrevious1: Int by mutableIntStateOf(DEFAULT_SCROLL_INDEX )
88
-
89
- private var beginFromPrevious1: Int by mutableIntStateOf(DEFAULT_SCROLL_INDEX )
90
- private var endFromPrevious1: Int by mutableStateOf(DEFAULT_SCROLL_INDEX )
91
-
92
68
/* * Perform a programmatic scroll. */
93
69
public fun programmaticScroll (
94
70
firstIndex : Int ,
@@ -98,16 +74,14 @@ public open class LazyListState {
98
74
require(firstIndex >= 0 )
99
75
if (! clobberUserScroll && userScrolled) return
100
76
77
+ strategy.scrollTo(firstIndex)
78
+
101
79
val previous = programmaticScrollIndex
102
80
this .programmaticScrollIndex = ScrollItemIndex (
103
81
id = previous.id + 1 ,
104
82
index = firstIndex,
105
83
animated = animated,
106
84
)
107
-
108
- val delta = (lastIndex - this .firstIndex)
109
- this .firstIndex = firstIndex
110
- this .lastIndex = firstIndex + delta
111
85
}
112
86
113
87
/* * React to a user-initiated scroll. */
@@ -116,85 +90,10 @@ public open class LazyListState {
116
90
userScrolled = true
117
91
}
118
92
119
- this .firstIndex = firstIndex
120
- this .lastIndex = lastIndex
93
+ strategy.onUserScroll(firstIndex, lastIndex)
121
94
}
122
95
123
96
public fun loadRange (itemCount : Int ): IntRange {
124
- // Ensure that the range includes `firstIndex` through `lastIndex`.
125
- var begin = firstIndex
126
- var end = lastIndex
127
-
128
- val isScrollingDown = firstIndexFromPrevious1 != DEFAULT_SCROLL_INDEX && firstIndexFromPrevious1 < firstIndex
129
- val isScrollingUp = firstIndexFromPrevious1 != DEFAULT_SCROLL_INDEX && firstIndexFromPrevious1 > firstIndex
130
- val hasStoppedScrolling = firstIndexFromPrevious2 != DEFAULT_SCROLL_INDEX && firstIndex == firstIndexFromPrevious1
131
- val wasScrollingDown = firstIndexFromPrevious1 > firstIndexFromPrevious2
132
- val wasScrollingUp = firstIndexFromPrevious1 < firstIndexFromPrevious2
133
-
134
- // Expand the range depending on scroll direction.
135
- when {
136
- // Ignore preloads.
137
- ! preloadItems -> {
138
- // No-op
139
- }
140
-
141
- isScrollingDown -> {
142
- begin - = scrollInProgressPreloadItemCount
143
- end + = primaryPreloadItemCount
144
- }
145
-
146
- isScrollingUp -> {
147
- begin - = primaryPreloadItemCount
148
- end + = scrollInProgressPreloadItemCount
149
- }
150
-
151
- hasStoppedScrolling && wasScrollingDown -> {
152
- begin - = secondaryPreloadItemCount
153
- end + = primaryPreloadItemCount
154
- }
155
-
156
- hasStoppedScrolling && wasScrollingUp -> {
157
- begin - = primaryPreloadItemCount
158
- end + = secondaryPreloadItemCount
159
- }
160
-
161
- // New.
162
- else -> {
163
- end + = defaultPreloadItemCount
164
- }
165
- }
166
-
167
- // On initial load, set lastIndex to the end of the loaded window.
168
- if (lastIndex == 0 ) {
169
- lastIndex = end
170
- }
171
-
172
- // If we're contiguous with the previous visible window,
173
- // don't rush to remove things from the previous range.
174
- if (beginFromPrevious1 != DEFAULT_SCROLL_INDEX &&
175
- endFromPrevious1 != DEFAULT_SCROLL_INDEX
176
- ) {
177
- // Case one: Contiguous scroll down
178
- if (begin in firstIndexFromPrevious1.. lastIndexFromPrevious1) {
179
- begin = beginFromPrevious1
180
- }
181
-
182
- // Case two: Contiguous scroll up
183
- if (end in firstIndexFromPrevious1.. lastIndexFromPrevious1) {
184
- end = endFromPrevious1
185
- }
186
- }
187
-
188
- begin = begin.coerceIn(0 , itemCount)
189
- end = end.coerceIn(0 , itemCount)
190
-
191
- this .firstIndexFromPrevious2 = firstIndexFromPrevious1
192
- this .firstIndexFromPrevious1 = firstIndex
193
- this .lastIndexFromPrevious1 = lastIndex
194
-
195
- this .beginFromPrevious1 = begin
196
- this .endFromPrevious1 = end
197
-
198
- return begin until end
97
+ return strategy.loadRange(itemCount)
199
98
}
200
99
}
0 commit comments