@onekeyfe/react-native-pager-view 6.7.0-alpha.375 → 6.7.0-alpha.382
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/README.md +5 -0
- package/android/src/androidTest/AndroidManifest.xml +7 -0
- package/android/src/androidTest/java/com/reactnativepagerview/CollapsiblePagerTabReservationTest.kt +247 -0
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerHost.kt +76 -21
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerNativeHeaders.kt +72 -7
- package/android/src/main/java/com/reactnativepagerview/CollapsiblePagerViewManager.kt +27 -0
- package/android/src/main/java/com/reactnativepagerview/NativeScrollerView.kt +50 -6
- package/docs/SPEC.md +101 -1
- package/ios/RNCCollapsiblePagerViewComponentView.mm +124 -12
- package/ios/tests/RNCCollapsiblePagerHitTestTests.mm +178 -0
- package/ios/tests/RNCCollapsiblePagerPressCancellationTests.mm +65 -0
- package/lib/module/CollapsiblePagerView.js +20 -4
- package/lib/module/CollapsiblePagerView.web.js +11 -2
- package/lib/module/CollapsiblePagerViewNativeComponent.ts +13 -0
- package/lib/typescript/src/CollapsiblePagerView.d.ts +6 -1
- package/lib/typescript/src/CollapsiblePagerView.web.d.ts +2 -0
- package/lib/typescript/src/CollapsiblePagerViewNativeComponent.d.ts +5 -0
- package/package.json +2 -2
- package/src/CollapsiblePagerView.tsx +68 -4
- package/src/CollapsiblePagerView.web.tsx +19 -2
- package/src/CollapsiblePagerViewNativeComponent.ts +13 -0
package/README.md
CHANGED
|
@@ -66,6 +66,11 @@ replacing its delegate. Android coordinates nested scroll with the active
|
|
|
66
66
|
Web scroll child can opt in by exposing a DOM element with
|
|
67
67
|
`data-collapsible-pager-scroll`. CSS scroll timelines drive header/sticky
|
|
68
68
|
movement, so React is not updated on each vertical scroll frame.
|
|
69
|
+
`pagerRef.current?.scrollHeaderBy(delta)` immediately moves the active page's
|
|
70
|
+
collapsible header by a finite logical distance, clamped to its measured range.
|
|
71
|
+
`scrollHeaderToOffset(offset)` targets an absolute collapse position, so a
|
|
72
|
+
keyboard's own scroll adjustment cannot add a second relative movement.
|
|
73
|
+
It is useful for revealing a focused field above a keyboard.
|
|
69
74
|
|
|
70
75
|
On Web, `WebCollapsiblePagerView` is an explicit root export of the existing Web
|
|
71
76
|
pager. It measures Header and sticky content when their heights are omitted.
|
package/android/src/androidTest/java/com/reactnativepagerview/CollapsiblePagerTabReservationTest.kt
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
package com.reactnativepagerview
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.content.pm.ApplicationInfo
|
|
6
|
+
import android.graphics.Bitmap
|
|
7
|
+
import android.graphics.Canvas
|
|
8
|
+
import android.graphics.Color
|
|
9
|
+
import android.test.InstrumentationTestCase
|
|
10
|
+
import android.view.MotionEvent
|
|
11
|
+
import android.view.View
|
|
12
|
+
import android.view.ViewGroup
|
|
13
|
+
import android.widget.HorizontalScrollView
|
|
14
|
+
import android.widget.TextView
|
|
15
|
+
import com.facebook.react.uimanager.PointerEvents
|
|
16
|
+
import com.facebook.react.views.view.ReactViewGroup
|
|
17
|
+
import java.util.concurrent.atomic.AtomicInteger
|
|
18
|
+
import kotlin.math.roundToInt
|
|
19
|
+
|
|
20
|
+
class CollapsiblePagerHitTestActivity : Activity()
|
|
21
|
+
|
|
22
|
+
class CollapsiblePagerTabReservationTest : InstrumentationTestCase() {
|
|
23
|
+
fun testSegmentedNativeTabsTrackFractionalProgressAndReset() {
|
|
24
|
+
val context = instrumentation.targetContext
|
|
25
|
+
assertTrue(context.applicationInfo.flags and ApplicationInfo.FLAG_SUPPORTS_RTL != 0)
|
|
26
|
+
val activity = instrumentation.startActivitySync(
|
|
27
|
+
Intent(context, CollapsiblePagerHitTestActivity::class.java)
|
|
28
|
+
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
|
29
|
+
) as CollapsiblePagerHitTestActivity
|
|
30
|
+
val density = context.resources.displayMetrics.density
|
|
31
|
+
fun dp(value: Int) = (value * density).roundToInt()
|
|
32
|
+
lateinit var tab: CollapsiblePagerNativeTabBarView
|
|
33
|
+
lateinit var row: HorizontalScrollView
|
|
34
|
+
lateinit var content: ViewGroup
|
|
35
|
+
lateinit var track: View
|
|
36
|
+
lateinit var thumb: View
|
|
37
|
+
lateinit var buttons: List<TextView>
|
|
38
|
+
fun layout(width: Int = 320, height: Int = 56) {
|
|
39
|
+
tab.measure(View.MeasureSpec.makeMeasureSpec(dp(width), View.MeasureSpec.EXACTLY),
|
|
40
|
+
View.MeasureSpec.makeMeasureSpec(dp(height), View.MeasureSpec.EXACTLY))
|
|
41
|
+
tab.layout(0, 0, dp(width), dp(height))
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
instrumentation.runOnMainSync {
|
|
45
|
+
tab = CollapsiblePagerNativeTabBarView(context)
|
|
46
|
+
activity.setContentView(tab)
|
|
47
|
+
val presses = mutableListOf<String>()
|
|
48
|
+
tab.onItemPress = { _, key -> presses.add(key) }
|
|
49
|
+
tab.updateItemsJSON("""[{"key":"recent","title":"Recents"},""" +
|
|
50
|
+
"""{"key":"account","title":"Accounts"},""" +
|
|
51
|
+
"""{"key":"book","title":"Address Book"}]""")
|
|
52
|
+
tab.updateStyle(56.0, "segmented", 20.0, 12.0, 0.0, 14.0, null, 2.0, 0.0)
|
|
53
|
+
tab.updateColors(Color.LTGRAY, Color.BLACK, Color.GRAY, Color.WHITE)
|
|
54
|
+
layout()
|
|
55
|
+
row = tab.getChildAt(0) as HorizontalScrollView
|
|
56
|
+
content = row.getChildAt(0) as ViewGroup
|
|
57
|
+
track = content.getChildAt(0)
|
|
58
|
+
thumb = content.getChildAt(1)
|
|
59
|
+
buttons = (0 until content.childCount).map(content::getChildAt).filterIsInstance<TextView>()
|
|
60
|
+
assertEquals(3, buttons.size)
|
|
61
|
+
assertEquals(dp(20), track.left)
|
|
62
|
+
assertTrue(kotlin.math.abs(dp(32) - track.height) <= 1)
|
|
63
|
+
assertEquals(0, row.scrollX)
|
|
64
|
+
assertTrue(kotlin.math.abs(buttons[0].width - buttons[1].width) <= 1)
|
|
65
|
+
tab.setProgress(0f)
|
|
66
|
+
val firstX = thumb.left
|
|
67
|
+
tab.setProgress(1f)
|
|
68
|
+
val secondX = thumb.left
|
|
69
|
+
tab.setProgress(0.25f)
|
|
70
|
+
assertTrue(kotlin.math.abs(thumb.left - (firstX + (secondX - firstX) * 0.25f)) <= 1)
|
|
71
|
+
tab.setProgress(0.5f)
|
|
72
|
+
assertTrue(kotlin.math.abs(thumb.left - (firstX + secondX) / 2f) <= 1)
|
|
73
|
+
tab.setProgress(0f)
|
|
74
|
+
assertEquals(firstX, thumb.left)
|
|
75
|
+
buttons[1].performClick()
|
|
76
|
+
assertEquals(listOf("account"), presses)
|
|
77
|
+
|
|
78
|
+
tab.layoutDirection = View.LAYOUT_DIRECTION_RTL
|
|
79
|
+
}
|
|
80
|
+
instrumentation.waitForIdleSync()
|
|
81
|
+
instrumentation.runOnMainSync {
|
|
82
|
+
layout()
|
|
83
|
+
assertEquals(View.LAYOUT_DIRECTION_RTL, row.layoutDirection)
|
|
84
|
+
val rtlFirst = buttons[0].layoutParams as android.widget.FrameLayout.LayoutParams
|
|
85
|
+
val rtlSecond = buttons[1].layoutParams as android.widget.FrameLayout.LayoutParams
|
|
86
|
+
assertTrue(rtlFirst.leftMargin > rtlSecond.leftMargin)
|
|
87
|
+
val rtlGeometry = "rowDir=${row.layoutDirection} contentDir=${content.layoutDirection} " +
|
|
88
|
+
buttons.mapIndexed { index, button ->
|
|
89
|
+
val params = button.layoutParams as android.widget.FrameLayout.LayoutParams
|
|
90
|
+
"$index:${button.left}-${button.right} margin=${params.leftMargin}/${params.rightMargin} gravity=${params.gravity}"
|
|
91
|
+
}.joinToString(" ")
|
|
92
|
+
assertTrue(rtlGeometry, buttons[0].left >= buttons[1].right)
|
|
93
|
+
assertEquals(dp(20), track.left)
|
|
94
|
+
tab.setProgress(0f)
|
|
95
|
+
val rtlFirstX = thumb.left
|
|
96
|
+
tab.setProgress(1f)
|
|
97
|
+
val rtlSecondX = thumb.left
|
|
98
|
+
assertTrue(rtlFirstX > rtlSecondX)
|
|
99
|
+
tab.setProgress(0.5f)
|
|
100
|
+
assertTrue(kotlin.math.abs(thumb.left - (rtlFirstX + rtlSecondX) / 2f) <= 1)
|
|
101
|
+
tab.layoutDirection = View.LAYOUT_DIRECTION_LTR
|
|
102
|
+
}
|
|
103
|
+
instrumentation.waitForIdleSync()
|
|
104
|
+
instrumentation.runOnMainSync {
|
|
105
|
+
layout()
|
|
106
|
+
assertTrue(buttons[0].right <= buttons[1].left)
|
|
107
|
+
tab.updateItemsJSON("""[{"key":"recent","title":"Recents (8)"},""" +
|
|
108
|
+
"""{"key":"account","title":"Accounts"}]""")
|
|
109
|
+
layout()
|
|
110
|
+
val twoButtons = (0 until content.childCount).map(content::getChildAt).filterIsInstance<TextView>()
|
|
111
|
+
assertEquals(2, twoButtons.size)
|
|
112
|
+
assertTrue(kotlin.math.abs(twoButtons[0].width - track.width / 2) <= 1)
|
|
113
|
+
layout(width = 30)
|
|
114
|
+
assertEquals(dp(15), track.left)
|
|
115
|
+
assertEquals(0, track.width)
|
|
116
|
+
tab.updateStyle(56.0, "segmented", 20.0, 40.0, 0.0, 14.0, null, 2.0, 0.0)
|
|
117
|
+
layout(width = 30)
|
|
118
|
+
assertTrue(kotlin.math.abs(dp(28) - track.top) <= 1)
|
|
119
|
+
assertEquals(0, track.height)
|
|
120
|
+
assertEquals(View.GONE, thumb.visibility)
|
|
121
|
+
tab.updateItemsJSON("[]")
|
|
122
|
+
assertEquals(View.GONE, tab.visibility)
|
|
123
|
+
tab.updateItemsJSON("""[{"key":"recent","title":"Recents"}]""")
|
|
124
|
+
tab.updateStyle(44.0, "underline", 20.0, 0.0, 8.0, 16.0, null, 2.0, 0.0)
|
|
125
|
+
layout(height = 44)
|
|
126
|
+
assertEquals(View.GONE, track.visibility)
|
|
127
|
+
assertEquals(dp(2), thumb.height)
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
instrumentation.runOnMainSync { activity.finish() }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fun testTabViewportToolbarHitAndResetInBothDirections() {
|
|
135
|
+
val context = instrumentation.targetContext
|
|
136
|
+
assertTrue(context.applicationInfo.flags and ApplicationInfo.FLAG_SUPPORTS_RTL != 0)
|
|
137
|
+
val activity = instrumentation.startActivitySync(
|
|
138
|
+
Intent(context, CollapsiblePagerHitTestActivity::class.java)
|
|
139
|
+
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
|
140
|
+
) as CollapsiblePagerHitTestActivity
|
|
141
|
+
val toolbarPresses = AtomicInteger()
|
|
142
|
+
val tabPresses = AtomicInteger()
|
|
143
|
+
try {
|
|
144
|
+
instrumentation.runOnMainSync {
|
|
145
|
+
val density = context.resources.displayMetrics.density
|
|
146
|
+
fun dp(value: Int) = (value * density).roundToInt()
|
|
147
|
+
val width = dp(320)
|
|
148
|
+
val height = dp(500)
|
|
149
|
+
val reserved = dp(64)
|
|
150
|
+
val tabHeight = dp(44)
|
|
151
|
+
val host = CollapsiblePagerHost(context)
|
|
152
|
+
activity.setContentView(host)
|
|
153
|
+
assertTrue(host.isAttachedToWindow)
|
|
154
|
+
val sticky = ReactViewGroup(context).apply { pointerEvents = PointerEvents.BOX_NONE }
|
|
155
|
+
val fade = View(context).apply { setBackgroundColor(Color.RED) }
|
|
156
|
+
val toolbar = View(context).apply {
|
|
157
|
+
setBackgroundColor(Color.BLUE)
|
|
158
|
+
isClickable = true
|
|
159
|
+
}
|
|
160
|
+
sticky.addView(fade)
|
|
161
|
+
sticky.addView(toolbar)
|
|
162
|
+
host.addReactChild(View(context), 0)
|
|
163
|
+
host.addReactChild(sticky, 1)
|
|
164
|
+
host.headerHeightPx = 0
|
|
165
|
+
host.stickyHeaderHeightPx = tabHeight
|
|
166
|
+
host.nativeTabBarBackgroundColor = Color.GREEN
|
|
167
|
+
host.applyNativeHeaderStyle()
|
|
168
|
+
host.updateNativeTabBarItems(
|
|
169
|
+
"""[{"key":"a","title":"Overview"},{"key":"b","title":"Ethereum"},""" +
|
|
170
|
+
"""{"key":"c","title":"BNB Chain"},{"key":"d","title":"Polygon"}]""",
|
|
171
|
+
)
|
|
172
|
+
val tab = (0 until host.childCount).map(host::getChildAt)
|
|
173
|
+
.first { it.javaClass.simpleName == "CollapsiblePagerNativeTabBarView" } as ViewGroup
|
|
174
|
+
val row = tab.getChildAt(0) as HorizontalScrollView
|
|
175
|
+
toolbar.setOnClickListener { toolbarPresses.incrementAndGet() }
|
|
176
|
+
host.onNativeTabPress = { _, _ -> tabPresses.incrementAndGet() }
|
|
177
|
+
|
|
178
|
+
fun layout() {
|
|
179
|
+
host.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
|
|
180
|
+
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
|
|
181
|
+
host.layout(0, 0, width, height)
|
|
182
|
+
}
|
|
183
|
+
fun tap(x: Int) {
|
|
184
|
+
val time = android.os.SystemClock.uptimeMillis()
|
|
185
|
+
val down = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN,
|
|
186
|
+
x.toFloat(), (tabHeight / 2).toFloat(), 0)
|
|
187
|
+
val up = MotionEvent.obtain(time, time + 20, MotionEvent.ACTION_UP,
|
|
188
|
+
x.toFloat(), (tabHeight / 2).toFloat(), 0)
|
|
189
|
+
host.dispatchTouchEvent(down)
|
|
190
|
+
host.dispatchTouchEvent(up)
|
|
191
|
+
down.recycle()
|
|
192
|
+
up.recycle()
|
|
193
|
+
}
|
|
194
|
+
fun colorAt(x: Int): Int {
|
|
195
|
+
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
196
|
+
host.draw(Canvas(bitmap))
|
|
197
|
+
val color = bitmap.getPixel(x, tabHeight / 2)
|
|
198
|
+
bitmap.recycle()
|
|
199
|
+
return color
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
host.nativeTabBarTrailingReservedWidth = 64.0
|
|
203
|
+
layout()
|
|
204
|
+
fade.layout(width - reserved - dp(20), 0, width - reserved, tabHeight)
|
|
205
|
+
toolbar.layout(width - reserved, 0, width, tabHeight)
|
|
206
|
+
assertEquals(width - reserved, tab.width)
|
|
207
|
+
assertEquals(0, tab.left)
|
|
208
|
+
assertEquals(width, sticky.width)
|
|
209
|
+
assertEquals(Color.RED, colorAt(width - reserved - dp(10)))
|
|
210
|
+
tap(width - dp(10))
|
|
211
|
+
tap(dp(28))
|
|
212
|
+
host.updateNativeTabProgress(3, 0f)
|
|
213
|
+
val content = row.getChildAt(0) as ViewGroup
|
|
214
|
+
val lastTab = (0 until content.childCount).map(content::getChildAt)
|
|
215
|
+
.filterIsInstance<TextView>().last()
|
|
216
|
+
assertTrue(lastTab.left >= row.scrollX - dp(1))
|
|
217
|
+
assertTrue(lastTab.right <= row.scrollX + row.width + dp(1))
|
|
218
|
+
|
|
219
|
+
host.setPagerLayoutDirection(View.LAYOUT_DIRECTION_RTL)
|
|
220
|
+
host.nativeTabBarTrailingReservedWidth = 64.0
|
|
221
|
+
layout()
|
|
222
|
+
assertEquals(View.LAYOUT_DIRECTION_RTL, tab.layoutDirection)
|
|
223
|
+
toolbar.layout(0, 0, reserved, tabHeight)
|
|
224
|
+
fade.layout(reserved, 0, reserved + dp(20), tabHeight)
|
|
225
|
+
assertEquals(reserved, tab.left)
|
|
226
|
+
assertEquals(width, tab.right)
|
|
227
|
+
assertEquals(Color.RED, colorAt(reserved + dp(10)))
|
|
228
|
+
tap(dp(10))
|
|
229
|
+
|
|
230
|
+
host.nativeTabBarTrailingReservedWidth = 500.0
|
|
231
|
+
layout()
|
|
232
|
+
assertEquals(0, tab.width)
|
|
233
|
+
host.nativeTabBarTrailingReservedWidth = 0.0
|
|
234
|
+
layout()
|
|
235
|
+
assertEquals(0, tab.left)
|
|
236
|
+
assertEquals(width, tab.width)
|
|
237
|
+
assertTrue(host.indexOfChild(sticky) < host.indexOfChild(tab))
|
|
238
|
+
}
|
|
239
|
+
instrumentation.waitForIdleSync()
|
|
240
|
+
assertEquals(2, toolbarPresses.get())
|
|
241
|
+
assertEquals(1, tabPresses.get())
|
|
242
|
+
} finally {
|
|
243
|
+
instrumentation.runOnMainSync { activity.finish() }
|
|
244
|
+
instrumentation.waitForIdleSync()
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -142,7 +142,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
142
142
|
private var transitionSourceRecyclerAnchor: RecyclerReadingAnchor? = null
|
|
143
143
|
private val commandBodyKeys = HashSet<String>()
|
|
144
144
|
private var explicitTopCommand: Pair<String, PageRestoreRequest>? = null
|
|
145
|
-
private class PagerTopRequest(view: View, val key: String, val pageIndex: Int, val generation: Int, val
|
|
145
|
+
private class PagerTopRequest(view: View, val key: String, val pageIndex: Int, val generation: Int, val logicalOffset: Int, val token: Int) {
|
|
146
146
|
val view = WeakReference(view)
|
|
147
147
|
}
|
|
148
148
|
private var pagerTopRequest: PagerTopRequest? = null
|
|
@@ -293,7 +293,23 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
293
293
|
var onNativeSubHeaderPress: ((Int, String) -> Unit)? = null
|
|
294
294
|
|
|
295
295
|
var nativeTabBarHeight = 44.0
|
|
296
|
+
var nativeTabBarVariant = "underline"
|
|
297
|
+
var nativeTabBarTrailingReservedWidth = 0.0
|
|
298
|
+
set(value) {
|
|
299
|
+
field = if (value.isFinite() && value >= 0.0) value else 0.0
|
|
300
|
+
bringNativeHeadersToFront()
|
|
301
|
+
requestLayout()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private fun nativeTabReservationPx(width: Int): Int {
|
|
305
|
+
val reserved = nativeTabBarTrailingReservedWidth
|
|
306
|
+
val density = resources.displayMetrics.density
|
|
307
|
+
return if (reserved.isFinite() && reserved > 0.0 && width > 0 && density > 0f) {
|
|
308
|
+
(reserved.coerceAtMost(width / density.toDouble()) * density).roundToInt()
|
|
309
|
+
} else 0
|
|
310
|
+
}
|
|
296
311
|
var nativeTabBarContentPaddingHorizontal = 20.0
|
|
312
|
+
var nativeTabBarContentPaddingVertical = 0.0
|
|
297
313
|
var nativeTabBarItemSpacing = 8.0
|
|
298
314
|
var nativeTabBarFontSize = 16.0
|
|
299
315
|
var nativeTabBarFontFamily: String? = null
|
|
@@ -416,7 +432,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
416
432
|
fun applyNativeHeaderStyle() {
|
|
417
433
|
nativeTabBarView.updateStyle(
|
|
418
434
|
nativeTabBarHeight,
|
|
435
|
+
nativeTabBarVariant,
|
|
419
436
|
nativeTabBarContentPaddingHorizontal,
|
|
437
|
+
nativeTabBarContentPaddingVertical,
|
|
420
438
|
nativeTabBarItemSpacing,
|
|
421
439
|
nativeTabBarFontSize,
|
|
422
440
|
nativeTabBarFontFamily,
|
|
@@ -442,6 +460,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
442
460
|
private fun bringNativeHeadersToFront() {
|
|
443
461
|
nativeTabBarView.bringToFront()
|
|
444
462
|
nativeSubHeaderView.bringToFront()
|
|
463
|
+
if (nativeTabBarTrailingReservedWidth > 0.0) stickyHeaderView?.bringToFront()
|
|
445
464
|
}
|
|
446
465
|
|
|
447
466
|
fun addReactChild(child: View, index: Int) {
|
|
@@ -689,7 +708,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
689
708
|
|
|
690
709
|
private fun layoutPagerIfRequested() {
|
|
691
710
|
val recycler = pager.getChildAt(0) as? RecyclerView ?: return
|
|
692
|
-
if (!recycler.isLayoutRequested || recycler.isComputingLayout || recycler.width <= 0 || recycler.height <= 0) return
|
|
711
|
+
if (!recycler.isLayoutRequested || recycler.isLayoutSuppressed || recycler.isComputingLayout || recycler.width <= 0 || recycler.height <= 0) return
|
|
693
712
|
// OneKey patch: a non-animated page command queues RecyclerView layout, but RN
|
|
694
713
|
// can stop that request after navigation. Drain it before showing the page.
|
|
695
714
|
recycler.measure(
|
|
@@ -712,6 +731,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
712
731
|
layout(left, top, right, bottom)
|
|
713
732
|
}
|
|
714
733
|
|
|
734
|
+
private fun isPagerRecyclerLayoutSuppressed(): Boolean =
|
|
735
|
+
(pager.getChildAt(0) as? RecyclerView)?.isLayoutSuppressed == true
|
|
736
|
+
|
|
715
737
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
716
738
|
val width = MeasureSpec.getSize(widthMeasureSpec)
|
|
717
739
|
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
@@ -722,10 +744,12 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
722
744
|
return
|
|
723
745
|
}
|
|
724
746
|
val pagerHeight = height + headerHeightPx
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
747
|
+
if (!isPagerRecyclerLayoutSuppressed()) {
|
|
748
|
+
pager.measure(
|
|
749
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
750
|
+
MeasureSpec.makeMeasureSpec(pagerHeight, MeasureSpec.EXACTLY),
|
|
751
|
+
)
|
|
752
|
+
}
|
|
729
753
|
headerView?.measure(
|
|
730
754
|
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
731
755
|
MeasureSpec.makeMeasureSpec(headerHeightPx, MeasureSpec.EXACTLY),
|
|
@@ -737,8 +761,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
737
761
|
val nativeTabHeight = if (nativeTabBarView.visibility == View.VISIBLE) {
|
|
738
762
|
nativeTabBarView.preferredHeightPx()
|
|
739
763
|
} else 0
|
|
764
|
+
val tabReservedWidth = nativeTabReservationPx(width)
|
|
740
765
|
nativeTabBarView.measure(
|
|
741
|
-
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
766
|
+
MeasureSpec.makeMeasureSpec(width - tabReservedWidth, MeasureSpec.EXACTLY),
|
|
742
767
|
MeasureSpec.makeMeasureSpec(nativeTabHeight, MeasureSpec.EXACTLY),
|
|
743
768
|
)
|
|
744
769
|
val nativeSubHeaderHeight = if (nativeSubHeaderView.visibility == View.VISIBLE) {
|
|
@@ -757,7 +782,9 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
757
782
|
// RN ancestors allow overflow; keep translated header content inside this viewport.
|
|
758
783
|
clipBounds = Rect(0, 0, width, height)
|
|
759
784
|
if (width <= 0 || height <= 0) return
|
|
760
|
-
|
|
785
|
+
if (!isPagerRecyclerLayoutSuppressed()) {
|
|
786
|
+
pager.layout(0, 0, width, height + headerHeightPx)
|
|
787
|
+
}
|
|
761
788
|
headerView?.layout(0, 0, width, headerHeightPx)
|
|
762
789
|
stickyHeaderView?.layout(
|
|
763
790
|
0,
|
|
@@ -766,7 +793,12 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
766
793
|
headerHeightPx + stickyHeaderHeightPx,
|
|
767
794
|
)
|
|
768
795
|
val nativeTabHeight = nativeTabBarView.measuredHeight
|
|
769
|
-
|
|
796
|
+
val tabReservedWidth = nativeTabReservationPx(width)
|
|
797
|
+
val tabLeft = if (nativeTabBarView.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
|
|
798
|
+
tabReservedWidth
|
|
799
|
+
} else 0
|
|
800
|
+
nativeTabBarView.layout(tabLeft, headerHeightPx, tabLeft + width - tabReservedWidth,
|
|
801
|
+
headerHeightPx + nativeTabHeight)
|
|
770
802
|
nativeSubHeaderView.layout(
|
|
771
803
|
0,
|
|
772
804
|
headerHeightPx + nativeTabHeight,
|
|
@@ -828,7 +860,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
828
860
|
}
|
|
829
861
|
if (!reached()) {
|
|
830
862
|
if (!isAttachedToWindow || !view.isAttachedToWindow || width <= 0 || height <= 0 ||
|
|
831
|
-
view.width <= 0 || view.height <= 0 || isInLayout || view.isInLayout || view.isComputingLayout ||
|
|
863
|
+
view.width <= 0 || view.height <= 0 || isInLayout || view.isInLayout || view.isLayoutSuppressed || view.isComputingLayout ||
|
|
832
864
|
pageKey(index) != key || primaryScrollableForPage(index) !== view || !isCurrentPageRestore(key, request)) return false
|
|
833
865
|
val previousSynthetic = syntheticPageRestore
|
|
834
866
|
syntheticPageRestore = true
|
|
@@ -987,13 +1019,33 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
987
1019
|
if (!settled) return
|
|
988
1020
|
pageOffsets[request.key] = 0
|
|
989
1021
|
if (view is RecyclerView) saveRecyclerReadingAnchor(request.key, view)
|
|
990
|
-
setHeaderOffset(
|
|
991
|
-
offsetDiag("pager-top-complete-
|
|
1022
|
+
setHeaderOffset(request.logicalOffset.coerceIn(0, headerHeightPx))
|
|
1023
|
+
offsetDiag("pager-top-complete-offset=${request.logicalOffset}-token=${request.token}", view, request.key)
|
|
992
1024
|
cancelPagerTop()
|
|
993
1025
|
}
|
|
994
1026
|
|
|
995
1027
|
internal fun scrollToTop(target: Int, animated: Boolean) {
|
|
996
|
-
if (target !in 0..1
|
|
1028
|
+
if (target !in 0..1) return
|
|
1029
|
+
scrollHeaderToOffset(if (target == 1) headerHeightPx else 0, animated)
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
internal fun scrollHeaderBy(delta: Double) {
|
|
1033
|
+
if (!delta.isFinite()) return
|
|
1034
|
+
val density = resources.displayMetrics.density.toDouble()
|
|
1035
|
+
val maxDip = headerHeightPx / density
|
|
1036
|
+
val deltaPx = (delta.coerceIn(-maxDip, maxDip) * density).roundToInt()
|
|
1037
|
+
scrollHeaderToOffset((headerOffsetPx.toLong() + deltaPx).coerceIn(0, headerHeightPx.toLong()).toInt(), false)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
internal fun scrollHeaderToOffset(offset: Double) {
|
|
1041
|
+
if (!offset.isFinite()) return
|
|
1042
|
+
val density = resources.displayMetrics.density.toDouble()
|
|
1043
|
+
val targetPx = (offset.coerceIn(0.0, headerHeightPx / density) * density).roundToInt()
|
|
1044
|
+
scrollHeaderToOffset(targetPx, false)
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
private fun scrollHeaderToOffset(logicalOffset: Int, animated: Boolean) {
|
|
1048
|
+
if (pager.scrollState != ViewPager2.SCROLL_STATE_IDLE || transitionHeaderOffsets != null ||
|
|
997
1049
|
syntheticPageRestore || selectedPage !in 0 until adapter.itemCount || !isAttachedToWindow) return
|
|
998
1050
|
val view = observedRecyclerView ?: observedOwnedScroller ?: observedNativeScroller ?: return
|
|
999
1051
|
val key = currentPageKey()
|
|
@@ -1020,11 +1072,12 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1020
1072
|
if (view is RecyclerView) deferredRecyclerGeometry.remove(view)
|
|
1021
1073
|
if (view is ScrollView) deferredScrollerGeometry.remove(view)
|
|
1022
1074
|
pageOffsets[key] = 0
|
|
1023
|
-
val
|
|
1075
|
+
val targetOffset = logicalOffset.coerceIn(0, headerHeightPx)
|
|
1076
|
+
val request = PagerTopRequest(view, key, selectedPage, attachmentGeneration, targetOffset, ++pagerTopToken)
|
|
1024
1077
|
pagerTopRequest = request
|
|
1025
1078
|
commandBodyKeys.add(key)
|
|
1026
|
-
setHeaderOffset(
|
|
1027
|
-
offsetDiag("pager-top-begin-
|
|
1079
|
+
setHeaderOffset(targetOffset)
|
|
1080
|
+
offsetDiag("pager-top-begin-offset=$targetOffset-animated=$animated-token=${request.token}", view, key)
|
|
1028
1081
|
dispatchingPagerTop = true
|
|
1029
1082
|
try {
|
|
1030
1083
|
when (view) {
|
|
@@ -1033,7 +1086,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1033
1086
|
if (animated && !recyclerIsAtBodyTop(view)) view.smoothScrollToPosition(0)
|
|
1034
1087
|
else if (manager != null && manager.orientation == RecyclerView.VERTICAL) {
|
|
1035
1088
|
manager.scrollToPositionWithOffset(0, 0)
|
|
1036
|
-
if (view.width > 0 && view.height > 0 && !isInLayout && !view.isInLayout && !view.isComputingLayout) {
|
|
1089
|
+
if (view.width > 0 && view.height > 0 && !isInLayout && !view.isInLayout && !view.isLayoutSuppressed && !view.isComputingLayout) {
|
|
1037
1090
|
view.measure(MeasureSpec.makeMeasureSpec(view.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(view.height, MeasureSpec.EXACTLY))
|
|
1038
1091
|
view.layout(view.left, view.top, view.right, view.bottom)
|
|
1039
1092
|
}
|
|
@@ -1638,7 +1691,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
1638
1691
|
if (anchorOffset != null) manager?.scrollToPositionWithOffset(first, anchorOffset)
|
|
1639
1692
|
// React Native can stop this nested requestLayout at its React-owned parent.
|
|
1640
1693
|
// Commit the changed inset before drawing instead of waiting for a data update.
|
|
1641
|
-
if (recycler.width > 0 && recycler.height > 0 && !recycler.isComputingLayout) {
|
|
1694
|
+
if (recycler.width > 0 && recycler.height > 0 && !recycler.isLayoutSuppressed && !recycler.isComputingLayout) {
|
|
1642
1695
|
recycler.forceLayout()
|
|
1643
1696
|
recycler.measure(
|
|
1644
1697
|
MeasureSpec.makeMeasureSpec(recycler.width, MeasureSpec.EXACTLY),
|
|
@@ -2012,14 +2065,16 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
2012
2065
|
}
|
|
2013
2066
|
}
|
|
2014
2067
|
|
|
2015
|
-
private fun headerRegionAt(y: Float): String? {
|
|
2068
|
+
private fun headerRegionAt(x: Float, y: Float): String? {
|
|
2016
2069
|
val headerBottom = headerHeightPx - headerOffsetPx
|
|
2017
2070
|
val stickyBottom = headerHeightPx + stickyHeaderHeightPx - headerOffsetPx
|
|
2018
2071
|
return when {
|
|
2019
2072
|
y < 0 || y >= stickyBottom -> null
|
|
2020
2073
|
y < headerBottom -> "header"
|
|
2021
2074
|
y < headerBottom + nativeTabBarView.measuredHeight &&
|
|
2022
|
-
nativeTabBarView.visibility == View.VISIBLE ->
|
|
2075
|
+
nativeTabBarView.visibility == View.VISIBLE ->
|
|
2076
|
+
if (x >= nativeTabBarView.left && x < nativeTabBarView.right) "primary-tab"
|
|
2077
|
+
else "sticky-controls"
|
|
2023
2078
|
y < headerBottom + nativeTabBarView.measuredHeight + nativeSubHeaderView.measuredHeight &&
|
|
2024
2079
|
nativeSubHeaderView.visibility == View.VISIBLE -> "secondary-header"
|
|
2025
2080
|
else -> "sticky-controls"
|
|
@@ -2128,7 +2183,7 @@ class CollapsiblePagerHost(context: Context) : NestedScrollableHost(context), Ne
|
|
|
2128
2183
|
pressCancelled = false
|
|
2129
2184
|
nativeGestureStarted = false
|
|
2130
2185
|
nestedNativeGestureStarted = false
|
|
2131
|
-
headerTouchRegion = headerRegionAt(event.y) ?: "none"
|
|
2186
|
+
headerTouchRegion = headerRegionAt(event.x, event.y) ?: "none"
|
|
2132
2187
|
headerTouchActive = headerTouchRegion != "none"
|
|
2133
2188
|
if (!headerTouchActive) return super.dispatchTouchEvent(event)
|
|
2134
2189
|
headerDownX = event.x
|
|
@@ -65,7 +65,11 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
65
65
|
) : HorizontalScrollView(context) {
|
|
66
66
|
private val density = resources.displayMetrics.density
|
|
67
67
|
private val content = FrameLayout(context)
|
|
68
|
+
private val track = View(context)
|
|
68
69
|
private val indicator = View(context)
|
|
70
|
+
private val trackBackground = GradientDrawable().apply {
|
|
71
|
+
shape = GradientDrawable.RECTANGLE
|
|
72
|
+
}
|
|
69
73
|
private val indicatorBackground = GradientDrawable().apply {
|
|
70
74
|
shape = GradientDrawable.RECTANGLE
|
|
71
75
|
}
|
|
@@ -75,6 +79,8 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
75
79
|
private var selectedKey = ""
|
|
76
80
|
private var rowHeightPx = dp(if (showsProgressIndicator) 44.0 else 42.0)
|
|
77
81
|
private var contentPaddingPx = dp(20.0)
|
|
82
|
+
private var contentPaddingVerticalPx = 0
|
|
83
|
+
private var segmented = false
|
|
78
84
|
private var itemSpacingPx = dp(8.0)
|
|
79
85
|
private var fontSize = if (showsProgressIndicator) 16.0 else 14.0
|
|
80
86
|
private var fontFamily: String? = null
|
|
@@ -96,7 +102,9 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
96
102
|
isFillViewport = true
|
|
97
103
|
overScrollMode = OVER_SCROLL_NEVER
|
|
98
104
|
clipToPadding = false
|
|
105
|
+
track.background = trackBackground
|
|
99
106
|
indicator.background = indicatorBackground
|
|
107
|
+
content.addView(track, FrameLayout.LayoutParams(0, 0))
|
|
100
108
|
content.addView(indicator, FrameLayout.LayoutParams(0, 0))
|
|
101
109
|
addView(
|
|
102
110
|
content,
|
|
@@ -149,17 +157,24 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
149
157
|
fontFamily: String?,
|
|
150
158
|
indicatorHeight: Double = 0.0,
|
|
151
159
|
indicatorBottom: Double = 0.0,
|
|
160
|
+
variant: String = "underline",
|
|
161
|
+
contentPaddingVertical: Double = 0.0,
|
|
152
162
|
) {
|
|
153
163
|
rowHeightPx = max(1, dp(rowHeight))
|
|
154
164
|
contentPaddingPx = max(0, dp(contentPadding))
|
|
165
|
+
contentPaddingVerticalPx = max(0, dp(contentPaddingVertical))
|
|
166
|
+
val wasSegmented = segmented
|
|
167
|
+
segmented = showsProgressIndicator && variant == "segmented"
|
|
155
168
|
itemSpacingPx = max(0, dp(itemSpacing))
|
|
156
169
|
this.fontSize = max(1.0, fontSize)
|
|
157
170
|
this.fontFamily = fontFamily
|
|
158
171
|
if (showsProgressIndicator) {
|
|
159
172
|
indicatorHeightPx = max(0, dp(indicatorHeight))
|
|
160
173
|
indicatorBottomPx = max(0, dp(indicatorBottom))
|
|
161
|
-
indicatorBackground.cornerRadius = indicatorHeightPx / 2f
|
|
174
|
+
indicatorBackground.cornerRadius = if (segmented) dp(14.0).toFloat() else indicatorHeightPx / 2f
|
|
162
175
|
}
|
|
176
|
+
track.visibility = if (segmented) VISIBLE else GONE
|
|
177
|
+
if (segmented && !wasSegmented) scrollTo(0, 0)
|
|
163
178
|
applyTypeface()
|
|
164
179
|
requestLayout()
|
|
165
180
|
}
|
|
@@ -171,8 +186,9 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
171
186
|
selectedBackgroundColor: Int,
|
|
172
187
|
indicatorColor: Int,
|
|
173
188
|
) {
|
|
174
|
-
setBackgroundColor(backgroundColor)
|
|
175
|
-
content.setBackgroundColor(backgroundColor)
|
|
189
|
+
setBackgroundColor(if (segmented) Color.TRANSPARENT else backgroundColor)
|
|
190
|
+
content.setBackgroundColor(if (segmented) Color.TRANSPARENT else backgroundColor)
|
|
191
|
+
trackBackground.setColor(backgroundColor)
|
|
176
192
|
this.activeTextColor = activeTextColor
|
|
177
193
|
this.inactiveTextColor = inactiveTextColor
|
|
178
194
|
this.selectedBackgroundColor = selectedBackgroundColor
|
|
@@ -213,6 +229,46 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
213
229
|
}
|
|
214
230
|
applyTypeface()
|
|
215
231
|
itemFrames.clear()
|
|
232
|
+
if (segmented) {
|
|
233
|
+
val actualPaddingHorizontal = min(contentPaddingPx, width / 2)
|
|
234
|
+
val actualPaddingVertical = min(contentPaddingVerticalPx, height / 2)
|
|
235
|
+
val trackLeft = actualPaddingHorizontal
|
|
236
|
+
val trackRight = max(trackLeft, width - contentPaddingPx)
|
|
237
|
+
val trackTop = actualPaddingVertical
|
|
238
|
+
val trackBottom = max(trackTop, height - contentPaddingVerticalPx)
|
|
239
|
+
val trackWidth = trackRight - trackLeft
|
|
240
|
+
val trackHeight = trackBottom - trackTop
|
|
241
|
+
trackBackground.cornerRadius = trackHeight / 2f
|
|
242
|
+
indicatorBackground.cornerRadius = max(0, trackHeight - dp(4.0)) / 2f
|
|
243
|
+
track.layoutParams = FrameLayout.LayoutParams(trackWidth, trackHeight).apply {
|
|
244
|
+
gravity = Gravity.TOP or Gravity.LEFT
|
|
245
|
+
leftMargin = trackLeft
|
|
246
|
+
topMargin = trackTop
|
|
247
|
+
}
|
|
248
|
+
buttons.forEachIndexed { index, button ->
|
|
249
|
+
val count = buttons.size
|
|
250
|
+
val first = trackLeft + trackWidth * index / count
|
|
251
|
+
val last = trackLeft + trackWidth * (index + 1) / count
|
|
252
|
+
val start = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) trackRight - (last - trackLeft) else first
|
|
253
|
+
val end = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) trackRight - (first - trackLeft) else last
|
|
254
|
+
val buttonWidth = max(0, end - start)
|
|
255
|
+
val thumbInset = dp(2.0)
|
|
256
|
+
itemFrames.add(intArrayOf(start, buttonWidth, start + thumbInset, max(0, buttonWidth - 2 * thumbInset)))
|
|
257
|
+
button.layoutParams = FrameLayout.LayoutParams(buttonWidth, trackHeight).apply {
|
|
258
|
+
gravity = Gravity.TOP or Gravity.LEFT
|
|
259
|
+
leftMargin = start
|
|
260
|
+
topMargin = trackTop
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
content.layoutParams = LayoutParams(width, height)
|
|
264
|
+
content.measure(
|
|
265
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
266
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
|
|
267
|
+
)
|
|
268
|
+
setMeasuredDimension(width, height)
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
track.layoutParams = FrameLayout.LayoutParams(0, 0)
|
|
216
272
|
var x = contentPaddingPx
|
|
217
273
|
buttons.forEachIndexed { index, button ->
|
|
218
274
|
val textWidth = ceil(button.paint.measureText(button.text.toString()).toDouble()).toInt()
|
|
@@ -260,6 +316,9 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
260
316
|
return super.onTouchEvent(event)
|
|
261
317
|
}
|
|
262
318
|
|
|
319
|
+
override fun onInterceptTouchEvent(event: MotionEvent): Boolean =
|
|
320
|
+
if (segmented) false else super.onInterceptTouchEvent(event)
|
|
321
|
+
|
|
263
322
|
private fun updatePresentation() {
|
|
264
323
|
if (buttons.isEmpty() || itemFrames.size != buttons.size) return
|
|
265
324
|
val selectedIndex: Int
|
|
@@ -275,14 +334,16 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
275
334
|
val interpolatedIndicatorWidth = from[3] + (to[3] - from[3]) * fraction
|
|
276
335
|
val indicatorX = interpolatedIndicatorX.roundToInt()
|
|
277
336
|
val indicatorWidth = interpolatedIndicatorWidth.roundToInt()
|
|
278
|
-
val
|
|
337
|
+
val actualPaddingVertical = min(contentPaddingVerticalPx, measuredHeight / 2)
|
|
338
|
+
val indicatorHeight = if (segmented) max(0, measuredHeight - 2 * actualPaddingVertical - dp(4.0)) else indicatorHeightPx
|
|
339
|
+
val indicatorTop = if (segmented) actualPaddingVertical + dp(2.0) else max(0, measuredHeight - indicatorBottomPx - indicatorHeightPx)
|
|
279
340
|
indicator.layout(
|
|
280
341
|
indicatorX,
|
|
281
342
|
indicatorTop,
|
|
282
343
|
indicatorX + indicatorWidth,
|
|
283
|
-
indicatorTop +
|
|
344
|
+
indicatorTop + indicatorHeight,
|
|
284
345
|
)
|
|
285
|
-
indicator.visibility = if (
|
|
346
|
+
indicator.visibility = if (indicatorHeight > 0) VISIBLE else GONE
|
|
286
347
|
scrollTargetCenterX = interpolatedIndicatorX + interpolatedIndicatorWidth / 2f
|
|
287
348
|
selectedIndex = clamped.roundToInt()
|
|
288
349
|
buttons.forEachIndexed { index, button ->
|
|
@@ -304,7 +365,7 @@ private class CollapsiblePagerHorizontalItemsView(
|
|
|
304
365
|
}
|
|
305
366
|
}
|
|
306
367
|
|
|
307
|
-
if (centerSelectionWhenIdle && !isTouchDragging && width > 0 && selectedIndex in itemFrames.indices) {
|
|
368
|
+
if (centerSelectionWhenIdle && !segmented && !isTouchDragging && width > 0 && selectedIndex in itemFrames.indices) {
|
|
308
369
|
val desired = (scrollTargetCenterX - width / 2f).roundToInt()
|
|
309
370
|
.coerceIn(0, max(0, content.measuredWidth - width))
|
|
310
371
|
scrollTo(desired, 0)
|
|
@@ -335,7 +396,9 @@ internal class CollapsiblePagerNativeTabBarView(context: Context) : FrameLayout(
|
|
|
335
396
|
|
|
336
397
|
fun updateStyle(
|
|
337
398
|
height: Double,
|
|
399
|
+
variant: String,
|
|
338
400
|
contentPadding: Double,
|
|
401
|
+
contentPaddingVertical: Double,
|
|
339
402
|
itemSpacing: Double,
|
|
340
403
|
fontSize: Double,
|
|
341
404
|
fontFamily: String?,
|
|
@@ -351,6 +414,8 @@ internal class CollapsiblePagerNativeTabBarView(context: Context) : FrameLayout(
|
|
|
351
414
|
fontFamily,
|
|
352
415
|
indicatorHeight,
|
|
353
416
|
indicatorBottom,
|
|
417
|
+
variant,
|
|
418
|
+
contentPaddingVertical,
|
|
354
419
|
)
|
|
355
420
|
requestLayout()
|
|
356
421
|
}
|