@onekeyfe/react-native-pager-view 3.0.119 → 3.0.121
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/build.gradle +1 -0
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +371 -1
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerNativeHeaders.kt +505 -0
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +109 -20
- package/android/src/main/java/com/reactnativepagerview/NestedScrollableHost.kt +4 -4
- package/android/src/main/java/com/reactnativepagerview/event/NativeHeaderPressEvent.kt +31 -0
- package/lib/module/CollapsiblePagerView.js +3 -3
- package/lib/typescript/src/CollapsiblePagerView.d.ts +4 -4
- package/package.json +3 -2
- package/src/CollapsiblePagerView.tsx +12 -7
package/android/build.gradle
CHANGED
|
@@ -226,6 +226,7 @@ dependencies {
|
|
|
226
226
|
implementation "com.facebook.react:react-android"
|
|
227
227
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
228
228
|
implementation 'androidx.viewpager2:viewpager2:1.1.0'
|
|
229
|
+
implementation project(":onekeyfe_react-native-native-logger")
|
|
229
230
|
}
|
|
230
231
|
|
|
231
232
|
if (isNewArchitectureEnabled()) {
|
|
@@ -2,9 +2,13 @@ package com.reactnativepagerview
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.graphics.Rect
|
|
5
|
+
import android.os.SystemClock
|
|
6
|
+
import android.view.MotionEvent
|
|
5
7
|
import android.view.View
|
|
8
|
+
import android.view.ViewConfiguration
|
|
6
9
|
import android.view.ViewGroup
|
|
7
10
|
import android.view.ViewTreeObserver
|
|
11
|
+
import android.widget.HorizontalScrollView
|
|
8
12
|
// OneKey patch: FrameLayout is inherited through NestedScrollableHost.
|
|
9
13
|
// import android.widget.FrameLayout
|
|
10
14
|
import androidx.core.view.NestedScrollingParent3
|
|
@@ -13,6 +17,8 @@ import androidx.core.view.ViewCompat
|
|
|
13
17
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
14
18
|
import androidx.recyclerview.widget.RecyclerView
|
|
15
19
|
import androidx.viewpager2.widget.ViewPager2
|
|
20
|
+
import com.facebook.react.uimanager.events.NativeGestureUtil
|
|
21
|
+
import com.margelo.nitro.nativelogger.OneKeyLog
|
|
16
22
|
import java.util.WeakHashMap
|
|
17
23
|
import kotlin.math.max
|
|
18
24
|
import kotlin.math.min
|
|
@@ -72,6 +78,13 @@ internal class CollapsiblePagerAdapter : RecyclerView.Adapter<ViewPagerViewHolde
|
|
|
72
78
|
// OneKey patch: Match the standard pager's nested horizontal gesture host.
|
|
73
79
|
// Original: class CollapsiblePagerHost(context: Context) : FrameLayout(context), NestedScrollingParent3 {
|
|
74
80
|
class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), NestedScrollingParent3 {
|
|
81
|
+
private enum class HeaderGestureOwner {
|
|
82
|
+
NONE,
|
|
83
|
+
LIST,
|
|
84
|
+
HORIZONTAL_CHILD,
|
|
85
|
+
HEADER_GUARD,
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
private data class RecyclerPadding(
|
|
76
89
|
var left: Int,
|
|
77
90
|
var top: Int,
|
|
@@ -83,6 +96,8 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
83
96
|
|
|
84
97
|
val pager = ViewPager2(context)
|
|
85
98
|
internal val adapter = CollapsiblePagerAdapter()
|
|
99
|
+
private val nativeTabBarView = CollapsiblePagerNativeTabBarView(context)
|
|
100
|
+
private val nativeSubHeaderView = CollapsiblePagerNativeSubHeaderView(context)
|
|
86
101
|
private val logicalChildren = ArrayList<View>()
|
|
87
102
|
private val nestedScrollingParentHelper = NestedScrollingParentHelper(this)
|
|
88
103
|
private val originalRecyclerPadding = WeakHashMap<RecyclerView, RecyclerPadding>()
|
|
@@ -91,8 +106,20 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
91
106
|
private var observedRecyclerView: RecyclerView? = null
|
|
92
107
|
private var observedScrollListener: RecyclerView.OnScrollListener? = null
|
|
93
108
|
private var attachmentGeneration = 0
|
|
109
|
+
private val touchSlopPx = ViewConfiguration.get(context).scaledTouchSlop
|
|
110
|
+
private val hostIdentity = System.identityHashCode(this)
|
|
94
111
|
private var headerView: View? = null
|
|
95
112
|
private var stickyHeaderView: View? = null
|
|
113
|
+
private var headerTouchActive = false
|
|
114
|
+
private var headerTouchRegion = "none"
|
|
115
|
+
private var headerGestureOwner = HeaderGestureOwner.NONE
|
|
116
|
+
private var headerDownX = 0f
|
|
117
|
+
private var headerDownY = 0f
|
|
118
|
+
private var headerDownEvent: MotionEvent? = null
|
|
119
|
+
private var headerHasHorizontalChild = false
|
|
120
|
+
private var forwardedRecycler: RecyclerView? = null
|
|
121
|
+
private var nativeGestureStarted = false
|
|
122
|
+
private var pressCancelled = false
|
|
96
123
|
private val pageContentLayoutListener = ViewTreeObserver.OnPreDrawListener {
|
|
97
124
|
if (width > 0 && height > 0 && isShown) layoutPagerIfRequested()
|
|
98
125
|
// Fabric can mount a retained list without another Android layout pass.
|
|
@@ -128,6 +155,22 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
128
155
|
var pageKeys: List<String> = emptyList()
|
|
129
156
|
var retainedPages: String = "[]"
|
|
130
157
|
var onHeaderOffsetChanged: (() -> Unit)? = null
|
|
158
|
+
var nativeSmoothHeaderScrollEnabled = false
|
|
159
|
+
var onNativeTabPress: ((Int, String) -> Unit)? = null
|
|
160
|
+
var onNativeSubHeaderPress: ((Int, String) -> Unit)? = null
|
|
161
|
+
|
|
162
|
+
var nativeTabBarHeight = 44.0
|
|
163
|
+
var nativeTabBarContentPaddingHorizontal = 20.0
|
|
164
|
+
var nativeTabBarItemSpacing = 8.0
|
|
165
|
+
var nativeTabBarFontSize = 16.0
|
|
166
|
+
var nativeTabBarFontFamily: String? = null
|
|
167
|
+
var nativeTabBarBackgroundColor = android.graphics.Color.TRANSPARENT
|
|
168
|
+
var nativeTabBarActiveTextColor = android.graphics.Color.BLACK
|
|
169
|
+
var nativeTabBarInactiveTextColor = android.graphics.Color.GRAY
|
|
170
|
+
var nativeTabBarIndicatorColor = android.graphics.Color.BLACK
|
|
171
|
+
var nativeTabBarIndicatorHeight = 2.0
|
|
172
|
+
var nativeTabBarIndicatorBottom = 0.0
|
|
173
|
+
var nativeSubHeaderSelectedBackgroundColor = android.graphics.Color.TRANSPARENT
|
|
131
174
|
|
|
132
175
|
init {
|
|
133
176
|
isSaveEnabled = false
|
|
@@ -139,6 +182,88 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
139
182
|
pager,
|
|
140
183
|
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT),
|
|
141
184
|
)
|
|
185
|
+
nativeTabBarView.onItemPress = { index, key -> onNativeTabPress?.invoke(index, key) }
|
|
186
|
+
nativeSubHeaderView.onItemPress = { index, key -> onNativeSubHeaderPress?.invoke(index, key) }
|
|
187
|
+
super.addView(nativeTabBarView)
|
|
188
|
+
super.addView(nativeSubHeaderView)
|
|
189
|
+
applyNativeHeaderStyle()
|
|
190
|
+
log("host-init generation=$attachmentGeneration")
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private fun log(message: String) {
|
|
194
|
+
OneKeyLog.debug("CollapsiblePager", "host=$hostIdentity $message")
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private fun outerPagerIndex(): Int {
|
|
198
|
+
var ancestor = parent as? View
|
|
199
|
+
while (ancestor != null) {
|
|
200
|
+
if (ancestor is ViewPager2) return ancestor.currentItem
|
|
201
|
+
ancestor = ancestor.parent as? View
|
|
202
|
+
}
|
|
203
|
+
return -1
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fun updateNativeTabBarItems(value: String?) {
|
|
207
|
+
nativeTabBarView.updateItemsJSON(value)
|
|
208
|
+
bringNativeHeadersToFront()
|
|
209
|
+
updateHeaderLayout()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
fun updateNativeSubHeader(value: String?) {
|
|
213
|
+
nativeSubHeaderView.updateConfigJSON(value)
|
|
214
|
+
applyNativeHeaderStyle()
|
|
215
|
+
bringNativeHeadersToFront()
|
|
216
|
+
updateHeaderLayout()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fun updateNativeTabProgress(position: Int, offset: Float) {
|
|
220
|
+
nativeTabBarView.setProgress(position + offset)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fun logPagerState(reason: String) {
|
|
224
|
+
log(
|
|
225
|
+
"pager-state reason=$reason inner=$selectedPage outer=${outerPagerIndex()} " +
|
|
226
|
+
"nativePages=${adapter.itemCount} attachedPages=${attachedPageCount()} " +
|
|
227
|
+
"headerOffset=$headerOffsetPx retained=$retainedPages",
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
fun setPagerLayoutDirection(layoutDirection: Int) {
|
|
232
|
+
pager.layoutDirection = layoutDirection
|
|
233
|
+
nativeTabBarView.layoutDirection = layoutDirection
|
|
234
|
+
nativeSubHeaderView.layoutDirection = layoutDirection
|
|
235
|
+
nativeSubHeaderView.requestLayout()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fun applyNativeHeaderStyle() {
|
|
239
|
+
nativeTabBarView.updateStyle(
|
|
240
|
+
nativeTabBarHeight,
|
|
241
|
+
nativeTabBarContentPaddingHorizontal,
|
|
242
|
+
nativeTabBarItemSpacing,
|
|
243
|
+
nativeTabBarFontSize,
|
|
244
|
+
nativeTabBarFontFamily,
|
|
245
|
+
nativeTabBarIndicatorHeight,
|
|
246
|
+
nativeTabBarIndicatorBottom,
|
|
247
|
+
)
|
|
248
|
+
nativeTabBarView.updateColors(
|
|
249
|
+
nativeTabBarBackgroundColor,
|
|
250
|
+
nativeTabBarActiveTextColor,
|
|
251
|
+
nativeTabBarInactiveTextColor,
|
|
252
|
+
nativeTabBarIndicatorColor,
|
|
253
|
+
)
|
|
254
|
+
nativeSubHeaderView.updateColors(
|
|
255
|
+
nativeTabBarBackgroundColor,
|
|
256
|
+
nativeTabBarActiveTextColor,
|
|
257
|
+
nativeTabBarInactiveTextColor,
|
|
258
|
+
nativeSubHeaderSelectedBackgroundColor,
|
|
259
|
+
nativeTabBarFontFamily,
|
|
260
|
+
)
|
|
261
|
+
updateHeaderLayout()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private fun bringNativeHeadersToFront() {
|
|
265
|
+
nativeTabBarView.bringToFront()
|
|
266
|
+
nativeSubHeaderView.bringToFront()
|
|
142
267
|
}
|
|
143
268
|
|
|
144
269
|
fun addReactChild(child: View, index: Int) {
|
|
@@ -160,7 +285,11 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
160
285
|
applyPendingInitialPage()
|
|
161
286
|
headerView?.bringToFront()
|
|
162
287
|
stickyHeaderView?.bringToFront()
|
|
288
|
+
bringNativeHeadersToFront()
|
|
163
289
|
requestLayout()
|
|
290
|
+
if (safeIndex >= PAGE_SLOT_OFFSET) {
|
|
291
|
+
log("page-attach slot=${safeIndex - PAGE_SLOT_OFFSET} nativePages=${adapter.itemCount}")
|
|
292
|
+
}
|
|
164
293
|
}
|
|
165
294
|
|
|
166
295
|
fun removeReactChild(child: View) {
|
|
@@ -179,6 +308,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
179
308
|
else -> {
|
|
180
309
|
findVerticalRecyclerView(child)?.let(::restoreRecyclerInsets)
|
|
181
310
|
adapter.removePage(child)
|
|
311
|
+
log("page-detach slot=${index - PAGE_SLOT_OFFSET} nativePages=${adapter.itemCount}")
|
|
182
312
|
}
|
|
183
313
|
}
|
|
184
314
|
}
|
|
@@ -195,6 +325,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
195
325
|
logicalChildren.clear()
|
|
196
326
|
adapter.clear()
|
|
197
327
|
hasAppliedInitialPage = false
|
|
328
|
+
log("pages-clear")
|
|
198
329
|
}
|
|
199
330
|
|
|
200
331
|
fun reactChildCount() = logicalChildren.size
|
|
@@ -285,6 +416,20 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
285
416
|
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
286
417
|
MeasureSpec.makeMeasureSpec(stickyHeaderHeightPx, MeasureSpec.EXACTLY),
|
|
287
418
|
)
|
|
419
|
+
val nativeTabHeight = if (nativeTabBarView.visibility == View.VISIBLE) {
|
|
420
|
+
nativeTabBarView.preferredHeightPx()
|
|
421
|
+
} else 0
|
|
422
|
+
nativeTabBarView.measure(
|
|
423
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
424
|
+
MeasureSpec.makeMeasureSpec(nativeTabHeight, MeasureSpec.EXACTLY),
|
|
425
|
+
)
|
|
426
|
+
val nativeSubHeaderHeight = if (nativeSubHeaderView.visibility == View.VISIBLE) {
|
|
427
|
+
nativeSubHeaderView.preferredHeightPx()
|
|
428
|
+
} else 0
|
|
429
|
+
nativeSubHeaderView.measure(
|
|
430
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
431
|
+
MeasureSpec.makeMeasureSpec(nativeSubHeaderHeight, MeasureSpec.EXACTLY),
|
|
432
|
+
)
|
|
288
433
|
setMeasuredDimension(width, height)
|
|
289
434
|
}
|
|
290
435
|
|
|
@@ -302,6 +447,14 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
302
447
|
width,
|
|
303
448
|
headerHeightPx + stickyHeaderHeightPx,
|
|
304
449
|
)
|
|
450
|
+
val nativeTabHeight = nativeTabBarView.measuredHeight
|
|
451
|
+
nativeTabBarView.layout(0, headerHeightPx, width, headerHeightPx + nativeTabHeight)
|
|
452
|
+
nativeSubHeaderView.layout(
|
|
453
|
+
0,
|
|
454
|
+
headerHeightPx + nativeTabHeight,
|
|
455
|
+
width,
|
|
456
|
+
headerHeightPx + nativeTabHeight + nativeSubHeaderView.measuredHeight,
|
|
457
|
+
)
|
|
305
458
|
applyHeaderOffset()
|
|
306
459
|
postForCurrentAttachment {
|
|
307
460
|
prepareAdjacentPages()
|
|
@@ -333,6 +486,8 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
333
486
|
pager.translationY = -offset
|
|
334
487
|
headerView?.translationY = -offset
|
|
335
488
|
stickyHeaderView?.translationY = -offset
|
|
489
|
+
nativeTabBarView.translationY = -offset
|
|
490
|
+
nativeSubHeaderView.translationY = -offset
|
|
336
491
|
}
|
|
337
492
|
|
|
338
493
|
private fun pageKey(index: Int): String = pageKeys.getOrNull(index) ?: "page-$index"
|
|
@@ -408,7 +563,10 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
408
563
|
}
|
|
409
564
|
observedScrollListener = listener
|
|
410
565
|
recycler.addOnScrollListener(listener)
|
|
411
|
-
|
|
566
|
+
log(
|
|
567
|
+
"list-observer-attach inner=$selectedPage key=${currentPageKey()} " +
|
|
568
|
+
"recycler=${System.identityHashCode(recycler)}",
|
|
569
|
+
)
|
|
412
570
|
}
|
|
413
571
|
|
|
414
572
|
private fun detachRecyclerObserver() {
|
|
@@ -416,6 +574,12 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
416
574
|
val recycler = observedRecyclerView
|
|
417
575
|
val listener = observedScrollListener
|
|
418
576
|
if (recycler != null && listener != null) recycler.removeOnScrollListener(listener)
|
|
577
|
+
if (recycler != null) {
|
|
578
|
+
log(
|
|
579
|
+
"list-observer-detach inner=$selectedPage key=${currentPageKey()} " +
|
|
580
|
+
"recycler=${System.identityHashCode(recycler)}",
|
|
581
|
+
)
|
|
582
|
+
}
|
|
419
583
|
observedRecyclerView = null
|
|
420
584
|
observedScrollListener = null
|
|
421
585
|
}
|
|
@@ -513,6 +677,197 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
513
677
|
recycler.setPadding(original.left, original.top, original.right, original.bottom)
|
|
514
678
|
}
|
|
515
679
|
|
|
680
|
+
private fun headerRegionAt(y: Float): String? {
|
|
681
|
+
val headerBottom = headerHeightPx - headerOffsetPx
|
|
682
|
+
val stickyBottom = headerHeightPx + stickyHeaderHeightPx - headerOffsetPx
|
|
683
|
+
return when {
|
|
684
|
+
y < 0 || y >= stickyBottom -> null
|
|
685
|
+
y < headerBottom -> "header"
|
|
686
|
+
y < headerBottom + nativeTabBarView.measuredHeight &&
|
|
687
|
+
nativeTabBarView.visibility == View.VISIBLE -> "primary-tab"
|
|
688
|
+
y < headerBottom + nativeTabBarView.measuredHeight + nativeSubHeaderView.measuredHeight &&
|
|
689
|
+
nativeSubHeaderView.visibility == View.VISIBLE -> "secondary-header"
|
|
690
|
+
else -> "sticky-controls"
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
private fun deepestChildAt(group: ViewGroup, x: Float, y: Float): View {
|
|
695
|
+
for (index in group.childCount - 1 downTo 0) {
|
|
696
|
+
val child = group.getChildAt(index)
|
|
697
|
+
if (child.visibility != View.VISIBLE || child.alpha <= 0f) continue
|
|
698
|
+
val localX = x + group.scrollX - child.left - child.translationX
|
|
699
|
+
val localY = y + group.scrollY - child.top - child.translationY
|
|
700
|
+
if (localX < 0 || localY < 0 || localX >= child.width || localY >= child.height) continue
|
|
701
|
+
return if (child is ViewGroup) deepestChildAt(child, localX, localY) else child
|
|
702
|
+
}
|
|
703
|
+
return group
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private fun hasHorizontalScrollOwner(x: Float, y: Float): Boolean {
|
|
707
|
+
var target: View? = deepestChildAt(this, x, y)
|
|
708
|
+
while (target != null && target !== this) {
|
|
709
|
+
if (
|
|
710
|
+
target is HorizontalScrollView ||
|
|
711
|
+
target.canScrollHorizontally(-1) ||
|
|
712
|
+
target.canScrollHorizontally(1) ||
|
|
713
|
+
(target is RecyclerView &&
|
|
714
|
+
(target.layoutManager as? LinearLayoutManager)?.orientation == RecyclerView.HORIZONTAL)
|
|
715
|
+
) {
|
|
716
|
+
return true
|
|
717
|
+
}
|
|
718
|
+
target = target.parent as? View
|
|
719
|
+
}
|
|
720
|
+
return false
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
private fun cancelHeaderPress(event: MotionEvent, owner: String) {
|
|
724
|
+
if (pressCancelled) return
|
|
725
|
+
pressCancelled = true
|
|
726
|
+
val cancel = MotionEvent.obtain(event).apply { action = MotionEvent.ACTION_CANCEL }
|
|
727
|
+
super.dispatchTouchEvent(cancel)
|
|
728
|
+
cancel.recycle()
|
|
729
|
+
if (!nativeGestureStarted) {
|
|
730
|
+
NativeGestureUtil.notifyNativeGestureStarted(this, event)
|
|
731
|
+
nativeGestureStarted = true
|
|
732
|
+
}
|
|
733
|
+
log(
|
|
734
|
+
"press-cancel owner=$owner region=$headerTouchRegion " +
|
|
735
|
+
"inner=$selectedPage outer=${outerPagerIndex()}",
|
|
736
|
+
)
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private fun dispatchToRecycler(recycler: RecyclerView, event: MotionEvent): Boolean {
|
|
740
|
+
val hostLocation = IntArray(2)
|
|
741
|
+
val recyclerLocation = IntArray(2)
|
|
742
|
+
getLocationOnScreen(hostLocation)
|
|
743
|
+
recycler.getLocationOnScreen(recyclerLocation)
|
|
744
|
+
val copy = MotionEvent.obtain(event)
|
|
745
|
+
copy.offsetLocation(
|
|
746
|
+
(hostLocation[0] - recyclerLocation[0]).toFloat(),
|
|
747
|
+
(hostLocation[1] - recyclerLocation[1]).toFloat(),
|
|
748
|
+
)
|
|
749
|
+
val handled = recycler.dispatchTouchEvent(copy)
|
|
750
|
+
copy.recycle()
|
|
751
|
+
return handled
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private fun beginForwardingToRecycler(event: MotionEvent): Boolean {
|
|
755
|
+
val recycler = recyclerViewForPage(selectedPage) ?: return false
|
|
756
|
+
forwardedRecycler = recycler
|
|
757
|
+
headerDownEvent?.let { down -> dispatchToRecycler(recycler, down) }
|
|
758
|
+
return dispatchToRecycler(recycler, event)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
private fun finishHeaderGesture(event: MotionEvent) {
|
|
762
|
+
val owner = headerGestureOwner.name.lowercase()
|
|
763
|
+
if (nativeGestureStarted || nestedNativeGestureStarted) {
|
|
764
|
+
NativeGestureUtil.notifyNativeGestureEnded(this, event)
|
|
765
|
+
nativeGestureStarted = false
|
|
766
|
+
nestedNativeGestureStarted = false
|
|
767
|
+
}
|
|
768
|
+
log(
|
|
769
|
+
"gesture-end action=${event.actionMasked} owner=$owner region=$headerTouchRegion " +
|
|
770
|
+
"inner=$selectedPage outer=${outerPagerIndex()} durationMs=" +
|
|
771
|
+
"${SystemClock.uptimeMillis() - event.downTime}",
|
|
772
|
+
)
|
|
773
|
+
headerDownEvent?.recycle()
|
|
774
|
+
headerDownEvent = null
|
|
775
|
+
headerTouchActive = false
|
|
776
|
+
headerTouchRegion = "none"
|
|
777
|
+
headerGestureOwner = HeaderGestureOwner.NONE
|
|
778
|
+
headerHasHorizontalChild = false
|
|
779
|
+
forwardedRecycler = null
|
|
780
|
+
pressCancelled = false
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
override fun dispatchTouchEvent(e: MotionEvent): Boolean {
|
|
784
|
+
val event = e
|
|
785
|
+
if (!nativeSmoothHeaderScrollEnabled) return super.dispatchTouchEvent(event)
|
|
786
|
+
|
|
787
|
+
when (event.actionMasked) {
|
|
788
|
+
MotionEvent.ACTION_DOWN -> {
|
|
789
|
+
headerDownEvent?.recycle()
|
|
790
|
+
headerDownEvent = null
|
|
791
|
+
headerGestureOwner = HeaderGestureOwner.NONE
|
|
792
|
+
forwardedRecycler = null
|
|
793
|
+
pressCancelled = false
|
|
794
|
+
nativeGestureStarted = false
|
|
795
|
+
nestedNativeGestureStarted = false
|
|
796
|
+
headerTouchRegion = headerRegionAt(event.y) ?: "none"
|
|
797
|
+
headerTouchActive = headerTouchRegion != "none"
|
|
798
|
+
if (!headerTouchActive) return super.dispatchTouchEvent(event)
|
|
799
|
+
headerDownX = event.x
|
|
800
|
+
headerDownY = event.y
|
|
801
|
+
headerDownEvent = MotionEvent.obtain(event)
|
|
802
|
+
headerHasHorizontalChild = hasHorizontalScrollOwner(event.x, event.y)
|
|
803
|
+
parent.requestDisallowInterceptTouchEvent(true)
|
|
804
|
+
log(
|
|
805
|
+
"gesture-begin region=$headerTouchRegion owner=pending " +
|
|
806
|
+
"horizontalChild=$headerHasHorizontalChild inner=$selectedPage " +
|
|
807
|
+
"outer=${outerPagerIndex()} headerOffset=$headerOffsetPx",
|
|
808
|
+
)
|
|
809
|
+
return super.dispatchTouchEvent(event)
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
MotionEvent.ACTION_MOVE -> {
|
|
813
|
+
if (!headerTouchActive) return super.dispatchTouchEvent(event)
|
|
814
|
+
if (headerGestureOwner == HeaderGestureOwner.NONE) {
|
|
815
|
+
val dx = event.x - headerDownX
|
|
816
|
+
val dy = event.y - headerDownY
|
|
817
|
+
if (kotlin.math.abs(dx) <= touchSlopPx && kotlin.math.abs(dy) <= touchSlopPx) {
|
|
818
|
+
return super.dispatchTouchEvent(event)
|
|
819
|
+
}
|
|
820
|
+
headerGestureOwner = when {
|
|
821
|
+
kotlin.math.abs(dy) > kotlin.math.abs(dx) -> HeaderGestureOwner.LIST
|
|
822
|
+
headerHasHorizontalChild -> HeaderGestureOwner.HORIZONTAL_CHILD
|
|
823
|
+
else -> HeaderGestureOwner.HEADER_GUARD
|
|
824
|
+
}
|
|
825
|
+
log(
|
|
826
|
+
"direction-lock owner=${headerGestureOwner.name.lowercase()} " +
|
|
827
|
+
"region=$headerTouchRegion dx=${dx.roundToInt()} dy=${dy.roundToInt()} " +
|
|
828
|
+
"inner=$selectedPage outer=${outerPagerIndex()}",
|
|
829
|
+
)
|
|
830
|
+
}
|
|
831
|
+
parent.requestDisallowInterceptTouchEvent(true)
|
|
832
|
+
return when (headerGestureOwner) {
|
|
833
|
+
HeaderGestureOwner.LIST -> {
|
|
834
|
+
cancelHeaderPress(event, "list")
|
|
835
|
+
val recycler = forwardedRecycler
|
|
836
|
+
if (recycler != null) {
|
|
837
|
+
dispatchToRecycler(recycler, event)
|
|
838
|
+
} else if (beginForwardingToRecycler(event)) {
|
|
839
|
+
true
|
|
840
|
+
} else {
|
|
841
|
+
headerGestureOwner = HeaderGestureOwner.HEADER_GUARD
|
|
842
|
+
log("gesture-list-unavailable region=$headerTouchRegion inner=$selectedPage")
|
|
843
|
+
true
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
HeaderGestureOwner.HORIZONTAL_CHILD -> super.dispatchTouchEvent(event)
|
|
847
|
+
HeaderGestureOwner.HEADER_GUARD -> {
|
|
848
|
+
cancelHeaderPress(event, "header-guard")
|
|
849
|
+
true
|
|
850
|
+
}
|
|
851
|
+
HeaderGestureOwner.NONE -> super.dispatchTouchEvent(event)
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
856
|
+
if (!headerTouchActive) return super.dispatchTouchEvent(event)
|
|
857
|
+
val handled = when (headerGestureOwner) {
|
|
858
|
+
HeaderGestureOwner.LIST -> forwardedRecycler?.let { recycler ->
|
|
859
|
+
dispatchToRecycler(recycler, event)
|
|
860
|
+
} ?: true
|
|
861
|
+
HeaderGestureOwner.HEADER_GUARD -> true
|
|
862
|
+
else -> super.dispatchTouchEvent(event)
|
|
863
|
+
}
|
|
864
|
+
finishHeaderGesture(event)
|
|
865
|
+
return handled
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return super.dispatchTouchEvent(event)
|
|
869
|
+
}
|
|
870
|
+
|
|
516
871
|
private fun isCurrentPageTarget(target: View): Boolean {
|
|
517
872
|
if (selectedPage !in 0 until adapter.itemCount) return false
|
|
518
873
|
return isDescendant(target, adapter.pageAt(selectedPage))
|
|
@@ -612,15 +967,30 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
612
967
|
attachmentGeneration += 1
|
|
613
968
|
restoredRecyclerKeys.clear()
|
|
614
969
|
viewTreeObserver.addOnPreDrawListener(pageContentLayoutListener)
|
|
970
|
+
log(
|
|
971
|
+
"host-attach generation=$attachmentGeneration inner=$selectedPage " +
|
|
972
|
+
"outer=${outerPagerIndex()} nativePages=${adapter.itemCount}",
|
|
973
|
+
)
|
|
615
974
|
}
|
|
616
975
|
|
|
617
976
|
override fun onDetachedFromWindow() {
|
|
618
977
|
attachmentGeneration += 1
|
|
978
|
+
headerDownEvent?.recycle()
|
|
979
|
+
headerDownEvent = null
|
|
980
|
+
headerTouchActive = false
|
|
981
|
+
forwardedRecycler = null
|
|
982
|
+
pressCancelled = false
|
|
983
|
+
nativeGestureStarted = false
|
|
984
|
+
nestedNativeGestureStarted = false
|
|
619
985
|
viewTreeObserver.removeOnPreDrawListener(pageContentLayoutListener)
|
|
620
986
|
detachRecyclerObserver()
|
|
621
987
|
for (recycler in originalRecyclerPadding.keys.toList()) {
|
|
622
988
|
restoreRecyclerInsets(recycler)
|
|
623
989
|
}
|
|
990
|
+
log(
|
|
991
|
+
"host-detach generation=$attachmentGeneration inner=$selectedPage " +
|
|
992
|
+
"outer=${outerPagerIndex()} nativePages=${adapter.itemCount}",
|
|
993
|
+
)
|
|
624
994
|
super.onDetachedFromWindow()
|
|
625
995
|
}
|
|
626
996
|
|