@onekeyfe/react-native-pager-view 3.0.172 → 3.0.173
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +21 -283
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +0 -8
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +0 -2
- package/android/src/test/java/com/reactnativepagerview/NativeScrollerPendingRestoreTest.kt +0 -11
- package/docs/SPEC.md +1 -50
- package/ios/RNCCollapsiblePagerViewComponentView.mm +25 -275
- package/ios/RNCNativeScrollerComponentView.h +0 -1
- package/ios/RNCNativeScrollerComponentView.mm +0 -42
- package/ios/tests/RNCCollapsiblePagerFirstFrameTests.mm +0 -257
- package/package.json +2 -2
|
@@ -123,7 +123,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
123
123
|
private val originalScrollerPadding = WeakHashMap<ScrollView, ScrollerPadding>()
|
|
124
124
|
private val restoredScrollerKeys = WeakHashMap<ScrollView, String>()
|
|
125
125
|
private val ownedScrollerKeys = WeakHashMap<NativeScrollerViewport, String>()
|
|
126
|
-
private val ownedScrollerRestoreRequests = HashMap<String, Pair<WeakReference<NativeScrollerViewport>, Int?>>()
|
|
127
126
|
private var foregroundRefreshIndicator: WeakReference<ImageView>? = null
|
|
128
127
|
private var refreshForegroundWasVisible = false
|
|
129
128
|
private val refreshForegroundMatrix = Matrix()
|
|
@@ -131,51 +130,13 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
131
130
|
private val refreshForegroundAncestors = ArrayList<View>()
|
|
132
131
|
private var refreshForegroundAncestorAlpha = 1f
|
|
133
132
|
private val pageOffsets = HashMap<String, Int>()
|
|
134
|
-
private val pageHeaderOffsets = HashMap<String, Int>()
|
|
135
|
-
private var transitionHeaderOffsets: Map<String, Int>? = null
|
|
136
|
-
private var transitionPageOffsets: Map<String, Int>? = null
|
|
137
|
-
private var transitionSourcePage = 0
|
|
138
|
-
private var transitionTargetPage: Int? = null
|
|
139
|
-
private var syntheticPageRestore = false
|
|
140
|
-
private var resumeTransitionSource: Int? = null
|
|
141
|
-
private val pendingPageRestores = HashSet<String>()
|
|
142
|
-
private class PageRestoreRequest(view: View, val pageIndex: Int, val generation: Int) {
|
|
143
|
-
val view = WeakReference(view)
|
|
144
|
-
}
|
|
145
|
-
private val pageRestoreRequests = HashMap<String, PageRestoreRequest>()
|
|
146
|
-
|
|
147
|
-
private fun registerPageRestore(key: String, view: View, pageIndex: Int): PageRestoreRequest? {
|
|
148
|
-
if (!nativeSmoothHeaderScrollEnabled) return null
|
|
149
|
-
val previous = pageRestoreRequests[key]?.view?.get()
|
|
150
|
-
if (previous is NativeScrollerViewport && previous !== view) previous.host.cancelPendingScrollRestore()
|
|
151
|
-
ownedScrollerRestoreRequests.remove(key)
|
|
152
|
-
return PageRestoreRequest(view, pageIndex, attachmentGeneration).also {
|
|
153
|
-
pageRestoreRequests[key] = it
|
|
154
|
-
pendingPageRestores.add(key)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
private fun isCurrentPageRestore(key: String, request: PageRestoreRequest?): Boolean =
|
|
159
|
-
!nativeSmoothHeaderScrollEnabled || (request != null && pageRestoreRequests[key] === request &&
|
|
160
|
-
key in pendingPageRestores && request.generation == attachmentGeneration &&
|
|
161
|
-
pageKey(request.pageIndex) == key)
|
|
162
|
-
|
|
163
|
-
private fun clearPageRestoreForView(key: String, view: View, request: PageRestoreRequest? = null) {
|
|
164
|
-
if (request != null && pageRestoreRequests[key] !== request) return
|
|
165
|
-
if (pageRestoreRequests[key]?.view?.get() !== view) return
|
|
166
|
-
pageRestoreRequests.remove(key)
|
|
167
|
-
pendingPageRestores.remove(key)
|
|
168
|
-
ownedScrollerRestoreRequests.remove(key)
|
|
169
|
-
}
|
|
170
133
|
private var observedRecyclerView: RecyclerView? = null
|
|
171
|
-
private var observedRecyclerKey: String? = null
|
|
172
134
|
private var observedScrollListener: RecyclerView.OnScrollListener? = null
|
|
173
135
|
private var observedOwnedScroller: NativeScrollerViewport? = null
|
|
174
136
|
private var observedOwnedScrollerKey: String? = null
|
|
175
137
|
private var observedOwnedScrollerListener: ViewTreeObserver.OnScrollChangedListener? = null
|
|
176
138
|
private var observedOwnedScrollerTreeObserver: ViewTreeObserver? = null
|
|
177
139
|
private var observedNativeScroller: ScrollView? = null
|
|
178
|
-
private var observedNativeScrollerKey: String? = null
|
|
179
140
|
private var observedNativeScrollerListener: ViewTreeObserver.OnScrollChangedListener? = null
|
|
180
141
|
private var observedNativeScrollerTreeObserver: ViewTreeObserver? = null
|
|
181
142
|
private var observedPageRemoved = false
|
|
@@ -228,18 +189,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
228
189
|
var headerOffsetPx = 0
|
|
229
190
|
private set
|
|
230
191
|
var pageKeys: List<String> = emptyList()
|
|
231
|
-
set(value) {
|
|
232
|
-
if (field == value) return
|
|
233
|
-
pageRestoreRequests.entries.toList().forEach { (key, request) ->
|
|
234
|
-
if ((value.getOrNull(request.pageIndex) ?: "page-${request.pageIndex}") != key) {
|
|
235
|
-
(request.view.get() as? NativeScrollerViewport)?.host?.cancelPendingScrollRestore()
|
|
236
|
-
pageRestoreRequests.remove(key)
|
|
237
|
-
pendingPageRestores.remove(key)
|
|
238
|
-
ownedScrollerRestoreRequests.remove(key)
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
field = value
|
|
242
|
-
}
|
|
243
192
|
var retainedPages: String = "[]"
|
|
244
193
|
var onHeaderOffsetChanged: (() -> Unit)? = null
|
|
245
194
|
var nativeSmoothHeaderScrollEnabled = false
|
|
@@ -281,19 +230,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
281
230
|
OneKeyLog.debug("CollapsiblePager", "host=$hostIdentity $message")
|
|
282
231
|
}
|
|
283
232
|
|
|
284
|
-
private var offsetDiagSequence = 0L
|
|
285
|
-
|
|
286
|
-
private fun offsetDiag(phase: String, view: View? = null, key: String = currentPageKey()) {
|
|
287
|
-
if ((context.applicationInfo.flags and android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) == 0) return
|
|
288
|
-
val safeKey = if (key in setOf("portfolio", "perps", "defi", "nft", "history")) key else "other"
|
|
289
|
-
val observed = observedOwnedScroller ?: observedNativeScroller ?: observedRecyclerView
|
|
290
|
-
OneKeyLog.debug("CollapsiblePager", "[HOME-OFFSET-DIAG] phase=$phase seq=${++offsetDiagSequence} " +
|
|
291
|
-
"wallMs=${System.currentTimeMillis()} monoMs=${SystemClock.uptimeMillis()} host=$hostIdentity " +
|
|
292
|
-
"generation=$attachmentGeneration page=$selectedPage key=$safeKey header=$headerOffsetPx headerHeight=$headerHeightPx " +
|
|
293
|
-
"view=${view?.let { System.identityHashCode(it) }} tag=${view?.id} observed=${observed?.let { System.identityHashCode(it) }} " +
|
|
294
|
-
"offset=${view?.let(::verticalScrollOffset)} cache=${pageOffsets[key]} boundsHeight=${view?.height}")
|
|
295
|
-
}
|
|
296
|
-
|
|
297
233
|
private fun outerPagerIndex(): Int {
|
|
298
234
|
var ancestor = parent as? View
|
|
299
235
|
while (ancestor != null) {
|
|
@@ -428,9 +364,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
428
364
|
fun removeAllReactChildren() {
|
|
429
365
|
releaseRefreshForeground()
|
|
430
366
|
detachPrimaryScrollObserver()
|
|
431
|
-
pageRestoreRequests.clear()
|
|
432
|
-
pendingPageRestores.clear()
|
|
433
|
-
ownedScrollerRestoreRequests.clear()
|
|
434
367
|
// Weak keys may disappear after size is read; Collection.toArray tolerates this.
|
|
435
368
|
for (recycler in originalRecyclerPadding.keys.toTypedArray()) {
|
|
436
369
|
restoreRecyclerInsets(recycler)
|
|
@@ -453,80 +386,16 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
453
386
|
|
|
454
387
|
fun reactChildAt(index: Int) = logicalChildren[index]
|
|
455
388
|
|
|
456
|
-
internal fun beginPageTransition(target: Int? = null) {
|
|
457
|
-
if (!nativeSmoothHeaderScrollEnabled || headerHeightPx <= 0) return
|
|
458
|
-
if (transitionHeaderOffsets == null) {
|
|
459
|
-
pageHeaderOffsets[currentPageKey()] = headerOffsetPx
|
|
460
|
-
detachPrimaryScrollObserver()
|
|
461
|
-
transitionSourcePage = selectedPage
|
|
462
|
-
transitionHeaderOffsets = pageHeaderOffsets.toMap()
|
|
463
|
-
transitionPageOffsets = pageOffsets.toMap()
|
|
464
|
-
}
|
|
465
|
-
if (target != null) transitionTargetPage = target
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
internal fun onPagerScrolled(position: Int, offset: Float) {
|
|
469
|
-
val headers = transitionHeaderOffsets ?: return
|
|
470
|
-
val target = transitionTargetPage
|
|
471
|
-
val first = if (target != null) transitionSourcePage else position
|
|
472
|
-
val second = target ?: (position + 1).coerceAtMost(adapter.itemCount - 1)
|
|
473
|
-
val fraction = if (target != null && target != first) {
|
|
474
|
-
((position + offset - first) / (target - first)).coerceIn(0f, 1f)
|
|
475
|
-
} else offset
|
|
476
|
-
val firstH = headers[pageKey(first)] ?: 0
|
|
477
|
-
val secondH = headers[pageKey(second)] ?: 0
|
|
478
|
-
syntheticPageRestore = true
|
|
479
|
-
setHeaderOffset((firstH + (secondH - firstH) * fraction).roundToInt())
|
|
480
|
-
syntheticPageRestore = false
|
|
481
|
-
updateNativeTabProgress(position, offset)
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
internal fun onPagerScrollStateChanged(state: Int) {
|
|
485
|
-
if (state == ViewPager2.SCROLL_STATE_DRAGGING || state == ViewPager2.SCROLL_STATE_SETTLING) {
|
|
486
|
-
beginPageTransition()
|
|
487
|
-
} else if (state == ViewPager2.SCROLL_STATE_IDLE && transitionHeaderOffsets != null) {
|
|
488
|
-
finishPageTransition(pager.currentItem)
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
private fun finishPageTransition(position: Int) {
|
|
493
|
-
val headers = transitionHeaderOffsets ?: return
|
|
494
|
-
detachPrimaryScrollObserver(recordOffset = false)
|
|
495
|
-
releaseRefreshForeground()
|
|
496
|
-
selectedPage = position.coerceIn(0, adapter.itemCount - 1)
|
|
497
|
-
syntheticPageRestore = true
|
|
498
|
-
setHeaderOffset(headers[currentPageKey()] ?: 0)
|
|
499
|
-
transitionHeaderOffsets = null
|
|
500
|
-
transitionPageOffsets = null
|
|
501
|
-
transitionTargetPage = null
|
|
502
|
-
syntheticPageRestore = false
|
|
503
|
-
pageHeaderOffsets[currentPageKey()] = headerOffsetPx
|
|
504
|
-
prepareAdjacentPages()
|
|
505
|
-
attachPrimaryScrollObserver()
|
|
506
|
-
}
|
|
507
|
-
|
|
508
389
|
fun selectPage(position: Int) {
|
|
509
|
-
offsetDiag("select-begin-target=$position", observedOwnedScroller ?: observedNativeScroller ?: observedRecyclerView)
|
|
510
390
|
pendingRefreshPull = null
|
|
511
391
|
if (adapter.itemCount == 0) return
|
|
512
|
-
if (nativeSmoothHeaderScrollEnabled && headerHeightPx > 0) {
|
|
513
|
-
if (transitionHeaderOffsets != null) return
|
|
514
|
-
beginPageTransition(position)
|
|
515
|
-
finishPageTransition(position)
|
|
516
|
-
return
|
|
517
|
-
}
|
|
518
392
|
detachPrimaryScrollObserver()
|
|
519
393
|
releaseRefreshForeground()
|
|
520
394
|
selectedPage = position.coerceIn(0, adapter.itemCount - 1)
|
|
521
395
|
val savedOffset = pageOffsets[currentPageKey()]
|
|
522
396
|
?: primaryScrollableForPage(selectedPage)?.let(::verticalScrollOffset)
|
|
523
397
|
?: 0
|
|
524
|
-
|
|
525
|
-
if (savedOffset > 0) {
|
|
526
|
-
offsetDiag("select-force-collapse-before")
|
|
527
|
-
setHeaderOffset(headerHeightPx)
|
|
528
|
-
offsetDiag("select-force-collapse-after")
|
|
529
|
-
}
|
|
398
|
+
if (savedOffset > 0) setHeaderOffset(headerHeightPx)
|
|
530
399
|
postForCurrentAttachment {
|
|
531
400
|
prepareAdjacentPages()
|
|
532
401
|
attachPrimaryScrollObserver()
|
|
@@ -668,14 +537,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
668
537
|
|
|
669
538
|
private fun setHeaderOffset(offset: Int) {
|
|
670
539
|
// val next = offset.coerceIn(0, headerHeightPx)
|
|
671
|
-
val
|
|
672
|
-
val maximum = if (transitionHeaderOffsets != null || pending) headerHeightPx else maximumHeaderOffset()
|
|
673
|
-
val next = offset.coerceIn(0, maximum)
|
|
540
|
+
val next = offset.coerceIn(0, maximumHeaderOffset())
|
|
674
541
|
if (headerOffsetPx == next) return
|
|
675
542
|
headerOffsetPx = next
|
|
676
|
-
if (nativeSmoothHeaderScrollEnabled && transitionHeaderOffsets == null && !syntheticPageRestore && !pending) {
|
|
677
|
-
pageHeaderOffsets[currentPageKey()] = next
|
|
678
|
-
}
|
|
679
543
|
applyHeaderOffset()
|
|
680
544
|
}
|
|
681
545
|
|
|
@@ -792,17 +656,13 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
792
656
|
}
|
|
793
657
|
|
|
794
658
|
private fun attachPrimaryScrollObserver() {
|
|
795
|
-
if (transitionHeaderOffsets != null) return
|
|
796
659
|
if (selectedPage !in 0 until adapter.itemCount) return
|
|
797
660
|
val previous = observedRecyclerView ?: observedOwnedScroller ?: observedNativeScroller
|
|
798
661
|
if (observedPageRemoved || previous != null && !isDescendant(previous, adapter.pageAt(selectedPage))) {
|
|
799
|
-
offsetDiag("primary-replacement-before-removed=$observedPageRemoved", previous)
|
|
800
662
|
observedPageRemoved = false
|
|
801
|
-
detachPrimaryScrollObserver(
|
|
663
|
+
detachPrimaryScrollObserver()
|
|
802
664
|
// A replacement scroller starts at the beginning while retaining header collapse.
|
|
803
|
-
|
|
804
|
-
if (!nativeSmoothHeaderScrollEnabled) pageOffsets[currentPageKey()] = 0
|
|
805
|
-
offsetDiag("primary-replacement-cache-after", previous)
|
|
665
|
+
pageOffsets[currentPageKey()] = 0
|
|
806
666
|
}
|
|
807
667
|
when (val scrollable = primaryScrollableForPage(selectedPage)) {
|
|
808
668
|
is RecyclerView -> attachRecyclerObserver(scrollable)
|
|
@@ -812,33 +672,24 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
812
672
|
}
|
|
813
673
|
|
|
814
674
|
private fun attachRecyclerObserver(recycler: RecyclerView) {
|
|
815
|
-
if (observedRecyclerView === recycler
|
|
675
|
+
if (observedRecyclerView === recycler) {
|
|
816
676
|
applyRecyclerInsets(recycler, selectedPage)
|
|
817
677
|
publishConsumedOffset(recycler)
|
|
818
678
|
return
|
|
819
679
|
}
|
|
820
680
|
|
|
821
681
|
detachPrimaryScrollObserver()
|
|
822
|
-
offsetDiag("recycler-attach-before", recycler)
|
|
823
682
|
observedRecyclerView = recycler
|
|
824
|
-
observedRecyclerKey = currentPageKey()
|
|
825
683
|
applyRecyclerInsets(recycler, selectedPage)
|
|
826
684
|
publishConsumedOffset(recycler)
|
|
827
|
-
val key = currentPageKey()
|
|
828
685
|
val listener = object : RecyclerView.OnScrollListener() {
|
|
829
686
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
|
830
|
-
if (observedScrollListener !== this || observedRecyclerKey != key ||
|
|
831
|
-
key != currentPageKey() || !isAttachedToWindow ||
|
|
832
|
-
transitionHeaderOffsets != null || syntheticPageRestore || key in pendingPageRestores) return
|
|
833
687
|
val contentOffset = recyclerView.computeVerticalScrollOffset()
|
|
834
|
-
|
|
835
|
-
pageOffsets[key] = contentOffset
|
|
688
|
+
pageOffsets[currentPageKey()] = contentOffset
|
|
836
689
|
// Nested touch scrolling moves list content only after the header has
|
|
837
690
|
// collapsed. Programmatic list jumps bypass those parent callbacks.
|
|
838
691
|
if (contentOffset > 0 && headerOffsetPx < headerHeightPx) {
|
|
839
|
-
offsetDiag("recycler-positive-collapse-before", recyclerView)
|
|
840
692
|
setHeaderOffset(headerHeightPx)
|
|
841
|
-
offsetDiag("recycler-positive-collapse-after", recyclerView)
|
|
842
693
|
}
|
|
843
694
|
}
|
|
844
695
|
}
|
|
@@ -854,11 +705,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
854
705
|
val recycler = observedRecyclerView
|
|
855
706
|
val listener = observedScrollListener
|
|
856
707
|
if (recycler != null) {
|
|
857
|
-
|
|
858
|
-
if (recordOffset && transitionPageOffsets == null) observedRecyclerKey?.let {
|
|
859
|
-
if (it !in pendingPageRestores) pageOffsets[it] = recycler.computeVerticalScrollOffset()
|
|
860
|
-
}
|
|
861
|
-
offsetDiag("recycler-detach-save-after", recycler)
|
|
708
|
+
if (recordOffset) pageOffsets[currentPageKey()] = recycler.computeVerticalScrollOffset()
|
|
862
709
|
publishConsumedOffset(recycler, remove = true)
|
|
863
710
|
}
|
|
864
711
|
if (recycler != null && listener != null) recycler.removeOnScrollListener(listener)
|
|
@@ -869,37 +716,28 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
869
716
|
)
|
|
870
717
|
}
|
|
871
718
|
observedRecyclerView = null
|
|
872
|
-
observedRecyclerKey = null
|
|
873
719
|
observedScrollListener = null
|
|
874
720
|
}
|
|
875
721
|
|
|
876
722
|
private fun attachNativeScrollerObserver(scroller: ScrollView) {
|
|
877
|
-
if (observedNativeScroller === scroller
|
|
723
|
+
if (observedNativeScroller === scroller) {
|
|
878
724
|
applyNativeScrollerInsets(scroller, selectedPage)
|
|
879
725
|
return
|
|
880
726
|
}
|
|
881
727
|
|
|
882
728
|
detachPrimaryScrollObserver()
|
|
883
|
-
offsetDiag("scrollview-attach-before", scroller)
|
|
884
729
|
observedNativeScroller = scroller
|
|
885
|
-
observedNativeScrollerKey = currentPageKey()
|
|
886
730
|
applyNativeScrollerInsets(scroller, selectedPage)
|
|
887
|
-
val key = currentPageKey()
|
|
888
731
|
val listener = object : ViewTreeObserver.OnScrollChangedListener {
|
|
889
732
|
override fun onScrollChanged() {
|
|
890
733
|
if (observedNativeScrollerListener !== this) {
|
|
891
734
|
removeScrollChangedListener(null, scroller, this)
|
|
892
735
|
return
|
|
893
736
|
}
|
|
894
|
-
if (key != currentPageKey() || !isAttachedToWindow ||
|
|
895
|
-
transitionHeaderOffsets != null || syntheticPageRestore || key in pendingPageRestores) return
|
|
896
737
|
val contentOffset = scroller.scrollY
|
|
897
|
-
|
|
898
|
-
pageOffsets[key] = contentOffset
|
|
738
|
+
pageOffsets[currentPageKey()] = contentOffset
|
|
899
739
|
if (contentOffset > 0 && headerOffsetPx < headerHeightPx) {
|
|
900
|
-
offsetDiag("scrollview-positive-collapse-before", scroller)
|
|
901
740
|
setHeaderOffset(headerHeightPx)
|
|
902
|
-
offsetDiag("scrollview-positive-collapse-after", scroller)
|
|
903
741
|
}
|
|
904
742
|
}
|
|
905
743
|
}
|
|
@@ -916,11 +754,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
916
754
|
private fun detachNativeScrollerObserver(recordOffset: Boolean) {
|
|
917
755
|
val scroller = observedNativeScroller
|
|
918
756
|
val listener = observedNativeScrollerListener
|
|
919
|
-
|
|
920
|
-
if (scroller != null && recordOffset && transitionPageOffsets == null) observedNativeScrollerKey?.let {
|
|
921
|
-
if (it !in pendingPageRestores) pageOffsets[it] = scroller.scrollY
|
|
922
|
-
}
|
|
923
|
-
offsetDiag("scrollview-detach-save-after", scroller)
|
|
757
|
+
if (scroller != null && recordOffset) pageOffsets[currentPageKey()] = scroller.scrollY
|
|
924
758
|
if (scroller != null && listener != null) {
|
|
925
759
|
removeScrollChangedListener(observedNativeScrollerTreeObserver, scroller, listener)
|
|
926
760
|
}
|
|
@@ -931,7 +765,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
931
765
|
)
|
|
932
766
|
}
|
|
933
767
|
observedNativeScroller = null
|
|
934
|
-
observedNativeScrollerKey = null
|
|
935
768
|
observedNativeScrollerListener = null
|
|
936
769
|
observedNativeScrollerTreeObserver = null
|
|
937
770
|
}
|
|
@@ -960,21 +793,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
960
793
|
val key = pageKey(pageIndex)
|
|
961
794
|
if (ownedScrollerKeys[scroller] == key) return
|
|
962
795
|
ownedScrollerKeys[scroller] = key
|
|
963
|
-
val restoreRequest = registerPageRestore(key, scroller, pageIndex)
|
|
964
|
-
if (nativeSmoothHeaderScrollEnabled) ownedScrollerRestoreRequests[key] = WeakReference(scroller) to null
|
|
965
|
-
val generation = attachmentGeneration
|
|
966
796
|
postForCurrentAttachment {
|
|
967
|
-
if (
|
|
968
|
-
scroller.
|
|
969
|
-
(key in pendingPageRestores && ownedScrollerRestoreRequests[key]?.first?.get() === scroller))) {
|
|
970
|
-
offsetDiag("owned-restore-before", scroller, key)
|
|
971
|
-
val saved = transitionPageOffsets?.get(key) ?: pageOffsets[key] ?: 0
|
|
972
|
-
scroller.host.restoreScrollOffset(saved)
|
|
973
|
-
if (nativeSmoothHeaderScrollEnabled) {
|
|
974
|
-
ownedScrollerRestoreRequests[key] = WeakReference(scroller) to generation
|
|
975
|
-
recordOwnedScrollerOffset(scroller, key)
|
|
976
|
-
}
|
|
977
|
-
offsetDiag("owned-restore-after", scroller, key)
|
|
797
|
+
if (ownedScrollerKeys[scroller] == key && scroller.isAttachedToWindow) {
|
|
798
|
+
scroller.host.restoreScrollOffset(pageOffsets[key] ?: 0)
|
|
978
799
|
}
|
|
979
800
|
}
|
|
980
801
|
}
|
|
@@ -982,10 +803,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
982
803
|
private fun restoreOwnedScrollerInsets(scroller: NativeScrollerViewport) {
|
|
983
804
|
scroller.stopMotion()
|
|
984
805
|
scroller.host.cancelPendingScrollRestore()
|
|
985
|
-
ownedScrollerKeys.remove(scroller)
|
|
986
|
-
clearPageRestoreForView(key, scroller)
|
|
987
|
-
if (ownedScrollerRestoreRequests[key]?.first?.get() === scroller) ownedScrollerRestoreRequests.remove(key)
|
|
988
|
-
}
|
|
806
|
+
ownedScrollerKeys.remove(scroller)
|
|
989
807
|
publishConsumedOffset(scroller, remove = true)
|
|
990
808
|
scroller.host.setPagerInset(this, null)
|
|
991
809
|
}
|
|
@@ -993,11 +811,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
993
811
|
private fun attachOwnedScrollerObserver(scroller: NativeScrollerViewport) {
|
|
994
812
|
if (observedOwnedScroller === scroller && observedOwnedScrollerKey == currentPageKey()) {
|
|
995
813
|
applyOwnedScrollerInsets(scroller, selectedPage)
|
|
996
|
-
if (nativeSmoothHeaderScrollEnabled) recordOwnedScrollerOffset(scroller, currentPageKey())
|
|
997
814
|
return
|
|
998
815
|
}
|
|
999
816
|
detachPrimaryScrollObserver()
|
|
1000
|
-
offsetDiag("owned-attach-before", scroller)
|
|
1001
817
|
observedOwnedScroller = scroller
|
|
1002
818
|
observedOwnedScrollerKey = currentPageKey()
|
|
1003
819
|
val key = currentPageKey()
|
|
@@ -1008,7 +824,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1008
824
|
removeScrollChangedListener(null, scroller, this)
|
|
1009
825
|
return
|
|
1010
826
|
}
|
|
1011
|
-
|
|
827
|
+
val contentOffset = scroller.scrollY
|
|
828
|
+
pageOffsets[key] = contentOffset
|
|
829
|
+
if (contentOffset > 0 && headerOffsetPx < headerHeightPx) setHeaderOffset(headerHeightPx)
|
|
1012
830
|
}
|
|
1013
831
|
}
|
|
1014
832
|
observedOwnedScrollerListener = listener
|
|
@@ -1017,36 +835,11 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1017
835
|
}
|
|
1018
836
|
}
|
|
1019
837
|
|
|
1020
|
-
private fun recordOwnedScrollerOffset(scroller: NativeScrollerViewport, key: String) {
|
|
1021
|
-
if (ownedScrollerKeys[scroller] != key || !isAttachedToWindow || !scroller.isAttachedToWindow) return
|
|
1022
|
-
if (key in pendingPageRestores) {
|
|
1023
|
-
// A false native pending flag before our posted request executes is not cancellation.
|
|
1024
|
-
val request = ownedScrollerRestoreRequests[key]
|
|
1025
|
-
if (request == null || request.first.get() !== scroller || request.second != attachmentGeneration ||
|
|
1026
|
-
scroller.width <= 0 || scroller.height <= 0 || scroller.host.hasPendingScrollRestore()) return
|
|
1027
|
-
clearPageRestoreForView(key, scroller)
|
|
1028
|
-
}
|
|
1029
|
-
if (observedOwnedScroller !== scroller || observedOwnedScrollerKey != key ||
|
|
1030
|
-
key != currentPageKey() || transitionHeaderOffsets != null || syntheticPageRestore) return
|
|
1031
|
-
val contentOffset = scroller.scrollY
|
|
1032
|
-
if (pageOffsets[key] != contentOffset) offsetDiag("owned-cache-before-new=$contentOffset", scroller, key)
|
|
1033
|
-
pageOffsets[key] = contentOffset
|
|
1034
|
-
if (contentOffset > 0 && headerOffsetPx < headerHeightPx) {
|
|
1035
|
-
offsetDiag("owned-positive-collapse-before", scroller, key)
|
|
1036
|
-
setHeaderOffset(headerHeightPx)
|
|
1037
|
-
offsetDiag("owned-positive-collapse-after", scroller, key)
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
838
|
private fun detachOwnedScrollerObserver(recordOffset: Boolean) {
|
|
1042
839
|
val scroller = observedOwnedScroller ?: return
|
|
1043
840
|
scroller.stopMotion()
|
|
1044
841
|
publishConsumedOffset(scroller, remove = true)
|
|
1045
|
-
|
|
1046
|
-
if (recordOffset && transitionPageOffsets == null) observedOwnedScrollerKey?.let {
|
|
1047
|
-
if (it !in pendingPageRestores) pageOffsets[it] = scroller.scrollY
|
|
1048
|
-
}
|
|
1049
|
-
offsetDiag("owned-detach-save-after", scroller)
|
|
842
|
+
if (recordOffset) observedOwnedScrollerKey?.let { pageOffsets[it] = scroller.scrollY }
|
|
1050
843
|
observedOwnedScrollerListener?.let {
|
|
1051
844
|
removeScrollChangedListener(observedOwnedScrollerTreeObserver, scroller, it)
|
|
1052
845
|
}
|
|
@@ -1100,32 +893,17 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1100
893
|
val key = pageKey(pageIndex)
|
|
1101
894
|
if (restoredRecyclerKeys[recycler] != key) {
|
|
1102
895
|
restoredRecyclerKeys[recycler] = key
|
|
1103
|
-
val restoreRequest = registerPageRestore(key, recycler, pageIndex)
|
|
1104
896
|
val generation = attachmentGeneration
|
|
1105
897
|
recycler.post {
|
|
1106
|
-
if (!isCurrentPageRestore(key, restoreRequest)) {
|
|
1107
|
-
offsetDiag("post-skip-cancelled-page=$pageIndex", recycler, key)
|
|
1108
|
-
return@post
|
|
1109
|
-
}
|
|
1110
898
|
if (!isAttachedToWindow || generation != attachmentGeneration ||
|
|
1111
899
|
restoredRecyclerKeys[recycler] != key ||
|
|
1112
900
|
originalRecyclerPadding[recycler] !== original) {
|
|
1113
901
|
if (restoredRecyclerKeys[recycler] == key) restoredRecyclerKeys.remove(recycler)
|
|
1114
|
-
clearPageRestoreForView(key, recycler, restoreRequest)
|
|
1115
902
|
return@post
|
|
1116
903
|
}
|
|
1117
|
-
val saved =
|
|
904
|
+
val saved = pageOffsets[key] ?: 0
|
|
1118
905
|
val current = recycler.computeVerticalScrollOffset()
|
|
1119
|
-
offsetDiag("recycler-restore-before-saved=$saved", recycler, key)
|
|
1120
|
-
if (nativeSmoothHeaderScrollEnabled && (recycler.height <= 0 || recycler.childCount == 0)) {
|
|
1121
|
-
restoredRecyclerKeys.remove(recycler)
|
|
1122
|
-
if (pageRestoreRequests[key] === restoreRequest) pageRestoreRequests.remove(key)
|
|
1123
|
-
return@post
|
|
1124
|
-
}
|
|
1125
906
|
if (saved != current) recycler.scrollBy(0, saved - current)
|
|
1126
|
-
if (nativeSmoothHeaderScrollEnabled) clearPageRestoreForView(key, recycler, restoreRequest)
|
|
1127
|
-
else pendingPageRestores.remove(key)
|
|
1128
|
-
offsetDiag("recycler-restore-after", recycler, key)
|
|
1129
907
|
}
|
|
1130
908
|
}
|
|
1131
909
|
}
|
|
@@ -1171,32 +949,16 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1171
949
|
val key = pageKey(pageIndex)
|
|
1172
950
|
if (restoredScrollerKeys[scroller] != key) {
|
|
1173
951
|
restoredScrollerKeys[scroller] = key
|
|
1174
|
-
val restoreRequest = registerPageRestore(key, scroller, pageIndex)
|
|
1175
952
|
val generation = attachmentGeneration
|
|
1176
953
|
scroller.post {
|
|
1177
|
-
if (!isCurrentPageRestore(key, restoreRequest)) {
|
|
1178
|
-
offsetDiag("post-skip-cancelled-page=$pageIndex", scroller, key)
|
|
1179
|
-
return@post
|
|
1180
|
-
}
|
|
1181
954
|
if (!isAttachedToWindow || generation != attachmentGeneration ||
|
|
1182
955
|
restoredScrollerKeys[scroller] != key ||
|
|
1183
956
|
originalScrollerPadding[scroller] !== original) {
|
|
1184
957
|
if (restoredScrollerKeys[scroller] == key) restoredScrollerKeys.remove(scroller)
|
|
1185
|
-
clearPageRestoreForView(key, scroller, restoreRequest)
|
|
1186
|
-
return@post
|
|
1187
|
-
}
|
|
1188
|
-
val saved = transitionPageOffsets?.get(key) ?: pageOffsets[key] ?: 0
|
|
1189
|
-
offsetDiag("scrollview-restore-before-saved=$saved", scroller, key)
|
|
1190
|
-
if (nativeSmoothHeaderScrollEnabled &&
|
|
1191
|
-
(scroller.height <= 0 || (scroller.getChildAt(0)?.height ?: 0) <= 0)) {
|
|
1192
|
-
restoredScrollerKeys.remove(scroller)
|
|
1193
|
-
if (pageRestoreRequests[key] === restoreRequest) pageRestoreRequests.remove(key)
|
|
1194
958
|
return@post
|
|
1195
959
|
}
|
|
960
|
+
val saved = pageOffsets[key] ?: 0
|
|
1196
961
|
if (scroller.scrollY != saved) scroller.scrollTo(scroller.scrollX, saved)
|
|
1197
|
-
if (nativeSmoothHeaderScrollEnabled) clearPageRestoreForView(key, scroller, restoreRequest)
|
|
1198
|
-
else pendingPageRestores.remove(key)
|
|
1199
|
-
offsetDiag("scrollview-restore-after", scroller, key)
|
|
1200
962
|
}
|
|
1201
963
|
}
|
|
1202
964
|
}
|
|
@@ -1255,7 +1017,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1255
1017
|
publishConsumedOffset(recycler, remove = true)
|
|
1256
1018
|
val original = originalRecyclerPadding.remove(recycler) ?: return
|
|
1257
1019
|
updateRecyclerPaddingOwnership(recycler, original)
|
|
1258
|
-
restoredRecyclerKeys.remove(recycler)
|
|
1020
|
+
restoredRecyclerKeys.remove(recycler)
|
|
1259
1021
|
recycler.clipToPadding = original.clipToPadding
|
|
1260
1022
|
recycler.setPadding(original.left, original.top, original.right, original.bottom)
|
|
1261
1023
|
}
|
|
@@ -1263,7 +1025,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1263
1025
|
private fun restoreNativeScrollerInsets(scroller: ScrollView) {
|
|
1264
1026
|
val original = originalScrollerPadding.remove(scroller) ?: return
|
|
1265
1027
|
updateScrollerPaddingOwnership(scroller, original)
|
|
1266
|
-
restoredScrollerKeys.remove(scroller)
|
|
1028
|
+
restoredScrollerKeys.remove(scroller)
|
|
1267
1029
|
if (scroller is ReactScrollView) {
|
|
1268
1030
|
val state = scroller.reactScrollViewScrollState
|
|
1269
1031
|
scroller.setScrollAwayPaddingEnabledUnstable(
|
|
@@ -1637,11 +1399,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1637
1399
|
override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {
|
|
1638
1400
|
if (type == ViewCompat.TYPE_TOUCH) pendingRefreshPull = null
|
|
1639
1401
|
nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type)
|
|
1640
|
-
if (type == ViewCompat.TYPE_TOUCH) {
|
|
1641
|
-
pageRestoreRequests.remove(currentPageKey())
|
|
1642
|
-
pendingPageRestores.remove(currentPageKey())
|
|
1643
|
-
ownedScrollerRestoreRequests.remove(currentPageKey())
|
|
1644
|
-
}
|
|
1645
1402
|
}
|
|
1646
1403
|
|
|
1647
1404
|
override fun onStopNestedScroll(target: View, type: Int) {
|
|
@@ -1735,13 +1492,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1735
1492
|
restoredRecyclerKeys.clear()
|
|
1736
1493
|
restoredScrollerKeys.clear()
|
|
1737
1494
|
viewTreeObserver.addOnPreDrawListener(pageContentLayoutListener)
|
|
1738
|
-
resumeTransitionSource?.let { source ->
|
|
1739
|
-
resumeTransitionSource = null
|
|
1740
|
-
if (source in 0 until adapter.itemCount) {
|
|
1741
|
-
pager.setCurrentItem(source, false)
|
|
1742
|
-
selectPage(source)
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
1495
|
log(
|
|
1746
1496
|
"host-attach generation=$attachmentGeneration inner=$selectedPage " +
|
|
1747
1497
|
"outer=${outerPagerIndex()} nativePages=${adapter.itemCount}",
|
|
@@ -1761,18 +1511,6 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1761
1511
|
nestedNativeGestureStarted = false
|
|
1762
1512
|
viewTreeObserver.removeOnPreDrawListener(pageContentLayoutListener)
|
|
1763
1513
|
detachPrimaryScrollObserver()
|
|
1764
|
-
pageRestoreRequests.clear()
|
|
1765
|
-
pendingPageRestores.clear()
|
|
1766
|
-
ownedScrollerRestoreRequests.clear()
|
|
1767
|
-
transitionHeaderOffsets?.let { headers ->
|
|
1768
|
-
resumeTransitionSource = transitionSourcePage
|
|
1769
|
-
syntheticPageRestore = true
|
|
1770
|
-
setHeaderOffset(headers[pageKey(transitionSourcePage)] ?: 0)
|
|
1771
|
-
syntheticPageRestore = false
|
|
1772
|
-
}
|
|
1773
|
-
transitionHeaderOffsets = null
|
|
1774
|
-
transitionPageOffsets = null
|
|
1775
|
-
transitionTargetPage = null
|
|
1776
1514
|
// Snapshot live keys before restoration removes their padding records.
|
|
1777
1515
|
for (recycler in originalRecyclerPadding.keys.toTypedArray()) {
|
|
1778
1516
|
restoreRecyclerInsets(recycler)
|
|
@@ -43,7 +43,6 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
|
|
|
43
43
|
)
|
|
44
44
|
host.pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
|
45
45
|
override fun onPageScrolled(position: Int, offset: Float, offsetPixels: Int) {
|
|
46
|
-
host.onPagerScrolled(position, offset)
|
|
47
46
|
dispatch(reactContext, host, PageScrollEvent(host.id, position, offset))
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -56,7 +55,6 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
|
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
override fun onPageScrollStateChanged(state: Int) {
|
|
59
|
-
host.onPagerScrollStateChanged(state)
|
|
60
58
|
val value = when (state) {
|
|
61
59
|
ViewPager2.SCROLL_STATE_IDLE -> "idle"
|
|
62
60
|
ViewPager2.SCROLL_STATE_DRAGGING -> "dragging"
|
|
@@ -326,19 +324,13 @@ class CollapsiblePagerViewManager : ViewGroupManager<CollapsiblePagerHost>(),
|
|
|
326
324
|
|
|
327
325
|
override fun setPage(view: CollapsiblePagerHost?, selectedPage: Int) {
|
|
328
326
|
if (view != null && selectedPage in 0 until view.adapter.itemCount) {
|
|
329
|
-
view.beginPageTransition(selectedPage)
|
|
330
327
|
view.pager.setCurrentItem(selectedPage, true)
|
|
331
|
-
if (view.pager.scrollState == ViewPager2.SCROLL_STATE_IDLE) {
|
|
332
|
-
view.onPagerScrollStateChanged(ViewPager2.SCROLL_STATE_IDLE)
|
|
333
|
-
}
|
|
334
328
|
}
|
|
335
329
|
}
|
|
336
330
|
|
|
337
331
|
override fun setPageWithoutAnimation(view: CollapsiblePagerHost?, selectedPage: Int) {
|
|
338
332
|
if (view != null && selectedPage in 0 until view.adapter.itemCount) {
|
|
339
|
-
view.beginPageTransition(selectedPage)
|
|
340
333
|
view.pager.setCurrentItem(selectedPage, false)
|
|
341
|
-
view.onPagerScrollStateChanged(ViewPager2.SCROLL_STATE_IDLE)
|
|
342
334
|
}
|
|
343
335
|
}
|
|
344
336
|
|
|
@@ -255,8 +255,6 @@ class NativeScrollerView(context: Context) : FrameLayout(context) {
|
|
|
255
255
|
|
|
256
256
|
internal fun cancelPendingScrollRestore() = pendingRestore.cancel()
|
|
257
257
|
|
|
258
|
-
internal fun hasPendingScrollRestore(): Boolean = pendingRestore.target != null
|
|
259
|
-
|
|
260
258
|
internal fun setRefreshOffset(value: Int) {
|
|
261
259
|
refreshOffset = value
|
|
262
260
|
applyRefreshOffset()
|
|
@@ -27,16 +27,5 @@ fun main() {
|
|
|
27
27
|
restore.cancel()
|
|
28
28
|
check(restore.target == null)
|
|
29
29
|
check(restore.anchor(120) == 120)
|
|
30
|
-
// Cancellation clears the old target without requiring the reached offset to change.
|
|
31
|
-
restore.request(target = 640, reached = 120)
|
|
32
|
-
check(restore.anchor(120) == 640)
|
|
33
|
-
restore.cancel()
|
|
34
|
-
check(restore.target == null)
|
|
35
|
-
check(restore.anchor(120) == 120)
|
|
36
|
-
// A fresh request owns its target after cancellation.
|
|
37
|
-
restore.request(target = 320, reached = 120)
|
|
38
|
-
check(restore.target == 320)
|
|
39
|
-
restore.settle(reached = 320)
|
|
40
|
-
check(restore.target == null)
|
|
41
30
|
println("Native scroller pending restore: clamp retry, partial growth, settle and cancel passed")
|
|
42
31
|
}
|