@onekeyfe/react-native-native-list 6.7.0-alpha.378 → 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/android/src/main/java/com/onekey/nativelist/HybridNativeList.kt +14 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +240 -11
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityDetailsView.kt +7 -4
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +6 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListButtonGroupRenderer.kt +102 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListContainerSlotAdapter.kt +12 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +87 -7
- package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +177 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +43 -14
- package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +25 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +33 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +22 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +3 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +69 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +55 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +1912 -107
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListPressPolicyTest.kt +1 -0
- package/docs/ROW_TEMPLATES.md +60 -9
- package/docs/SPEC.md +450 -5
- package/docs/STYLE_SPEC.md +6 -0
- package/ios/HybridNativeList.swift +21 -2
- package/ios/NativeListAccessoryStack.swift +227 -39
- package/ios/NativeListActivityDetailsView.swift +18 -8
- package/ios/NativeListActivityRenderer.swift +8 -2
- package/ios/NativeListButtonGroupRenderer.swift +89 -0
- package/ios/NativeListDataRowRenderer.swift +155 -27
- package/ios/NativeListDesignAssets.swift +3 -0
- package/ios/NativeListIdentityRenderer.swift +166 -30
- package/ios/NativeListLeadingVisual.swift +49 -14
- package/ios/NativeListMessageRenderer.swift +34 -1
- package/ios/NativeListModels.swift +42 -2
- package/ios/NativeListRendererRegistry.swift +1 -0
- package/ios/NativeListResolvedText.swift +2 -1
- package/ios/NativeListRowHost.swift +14 -0
- package/ios/NativeListSectionHeaderRenderer.swift +88 -0
- package/ios/RNCNativeListView.swift +1013 -55
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/icon.svg +4 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/icon.svg +3 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/icon.svg +3 -0
- package/ios/tests/NativeListActivityPresentationTests.swift +56 -0
- package/ios/tests/NativeListLifecycleTests.swift +2720 -3
- package/lib/module/NativeList.js +175 -28
- package/lib/module/NativeList.web.js +129 -30
- package/lib/module/index.js +1 -1
- package/lib/module/models.js +1 -0
- package/lib/module/selection.js +1 -1
- package/lib/module/validation.js +421 -8
- package/lib/module/web/NativeListWebEngine.js +507 -19
- package/lib/module/web/templates/ActivityRowRenderer.js +8 -7
- package/lib/module/web/templates/ButtonGroupRowRenderer.js +47 -0
- package/lib/module/web/templates/DataRowRenderer.js +40 -5
- package/lib/module/web/templates/IdentityRowRenderer.js +90 -4
- package/lib/module/web/templates/MessageRowRenderer.js +18 -2
- package/lib/module/web/templates/RowContainerStyle.js +1 -0
- package/lib/module/web/templates/RowRendererRegistry.js +3 -0
- package/lib/module/web/templates/SectionHeaderRowRenderer.js +36 -2
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +26 -0
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +20 -0
- package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +14 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +43 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/lib/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/lib/typescript/src/NativeList.d.ts +1 -1
- package/lib/typescript/src/NativeList.nitro.d.ts +2 -0
- package/lib/typescript/src/NativeList.types.d.ts +18 -0
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/models.d.ts +105 -9
- package/lib/typescript/src/validation.d.ts +12 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +32 -0
- package/lib/typescript/src/web/templates/ButtonGroupRowRenderer.d.ts +14 -0
- package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +1 -1
- package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +9 -3
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +26 -0
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +20 -0
- package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +14 -0
- package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +43 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/package.json +3 -3
- package/react-native-native-list.podspec +1 -0
- package/src/NativeList.nitro.ts +2 -0
- package/src/NativeList.tsx +313 -44
- package/src/NativeList.types.ts +22 -0
- package/src/NativeList.web.tsx +204 -48
- package/src/index.ts +2 -0
- package/src/models.ts +117 -3
- package/src/selection.ts +1 -0
- package/src/validation.ts +852 -6
- package/src/web/NativeListWebEngine.ts +803 -20
- package/src/web/templates/ActivityRowRenderer.ts +14 -6
- package/src/web/templates/ButtonGroupRowRenderer.ts +58 -0
- package/src/web/templates/DataRowRenderer.ts +93 -20
- package/src/web/templates/IdentityRowRenderer.ts +150 -5
- package/src/web/templates/MessageRowRenderer.ts +23 -7
- package/src/web/templates/RowContainerStyle.ts +2 -0
- package/src/web/templates/RowRendererRegistry.ts +11 -1
- package/src/web/templates/SectionHeaderRowRenderer.ts +47 -4
|
@@ -28,11 +28,19 @@ class HybridNativeList(context: ThemedReactContext) : HybridNativeListSpec() {
|
|
|
28
28
|
set(value) {
|
|
29
29
|
if (field == value) return
|
|
30
30
|
field = value
|
|
31
|
-
|
|
31
|
+
slotHeightsChanged = true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override var sidecarLayoutJson: String = ""
|
|
35
|
+
set(value) {
|
|
36
|
+
if (field == value) return
|
|
37
|
+
field = value
|
|
38
|
+
dispatchToUi { hostView.setSidecarLayoutJson(value) }
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
private var fontsChanged = false
|
|
35
42
|
private var snapshotChanged = false
|
|
43
|
+
private var slotHeightsChanged = false
|
|
36
44
|
|
|
37
45
|
override var fontFamiliesJson: String = "{}"
|
|
38
46
|
set(value) {
|
|
@@ -44,11 +52,14 @@ class HybridNativeList(context: ThemedReactContext) : HybridNativeListSpec() {
|
|
|
44
52
|
override fun afterUpdate() {
|
|
45
53
|
super.afterUpdate()
|
|
46
54
|
val fonts = if (fontsChanged) fontFamiliesJson else null
|
|
55
|
+
val slotHeights = if (slotHeightsChanged) containerSlotHeightsJson else null
|
|
47
56
|
val snapshot = if (snapshotChanged) snapshotJson else null
|
|
48
57
|
fontsChanged = false
|
|
58
|
+
slotHeightsChanged = false
|
|
49
59
|
snapshotChanged = false
|
|
50
60
|
dispatchToUi {
|
|
51
61
|
fonts?.let { hostView.setFontFamiliesJson(it) }
|
|
62
|
+
slotHeights?.let { hostView.setContainerSlotHeightsJson(it, snapshot) }
|
|
52
63
|
snapshot?.let { hostView.applySnapshot(it) }
|
|
53
64
|
}
|
|
54
65
|
}
|
|
@@ -90,6 +101,8 @@ class HybridNativeList(context: ThemedReactContext) : HybridNativeListSpec() {
|
|
|
90
101
|
set(value) { field = value; hostView.onEndReached = value }
|
|
91
102
|
override var onVisibleRangeChanged: ((payloadJson: String) -> Unit)? = null
|
|
92
103
|
set(value) { field = value; hostView.onVisibleRangeChanged = value }
|
|
104
|
+
override var onStructuralTransitionSettled: ((payloadJson: String) -> Unit)? = null
|
|
105
|
+
set(value) { field = value; hostView.onStructuralTransitionSettled = value }
|
|
93
106
|
|
|
94
107
|
override fun applySnapshot(snapshotJson: String) {
|
|
95
108
|
dispatchToUi { hostView.applySnapshot(snapshotJson) }
|
|
@@ -8,11 +8,14 @@ import android.graphics.drawable.StateListDrawable
|
|
|
8
8
|
import android.text.Spannable
|
|
9
9
|
import android.text.SpannableStringBuilder
|
|
10
10
|
import android.text.TextUtils
|
|
11
|
+
import android.text.TextPaint
|
|
11
12
|
import android.text.style.AbsoluteSizeSpan
|
|
12
13
|
import android.text.style.ForegroundColorSpan
|
|
14
|
+
import android.text.style.MetricAffectingSpan
|
|
13
15
|
import android.util.TypedValue
|
|
14
16
|
import android.view.Gravity
|
|
15
17
|
import android.view.View
|
|
18
|
+
import android.view.MotionEvent
|
|
16
19
|
import android.widget.LinearLayout
|
|
17
20
|
import android.widget.ProgressBar
|
|
18
21
|
import android.widget.TextView
|
|
@@ -29,11 +32,14 @@ import org.json.JSONObject
|
|
|
29
32
|
// Bounded controls only. Event ownership and epochs stay with the row host.
|
|
30
33
|
internal class NativeListAccessoryStack(context: android.content.Context, val fonts: NativeListFonts) : LinearLayout(context) {
|
|
31
34
|
private val trailingViews = List(2) { NativeListTextView(context) }
|
|
35
|
+
private val buttonDownEpochs = IntArray(2) { -1 }
|
|
36
|
+
private var bindingEpoch = 0
|
|
32
37
|
private val trailingIcons = List(2) { OneKeyIconView(context) }
|
|
33
38
|
private val checkbox = OneKeyCheckboxView(context)
|
|
34
39
|
private val spinner = ProgressBar(context)
|
|
35
40
|
private var boundCheckboxData: JSONObject? = null
|
|
36
41
|
private val semanticValueViews = mutableListOf<TextView>()
|
|
42
|
+
private val segmentedValuePairs = mutableMapOf<TextView, JSONObject>()
|
|
37
43
|
private var selectorUsesSourceScale = false
|
|
38
44
|
private var checkboxUsesSelectorStyle = false
|
|
39
45
|
private var checkboxCheckedColor = Color.BLACK
|
|
@@ -66,6 +72,19 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
66
72
|
info.viewIdResourceName = host.getTag(com.facebook.react.R.id.react_test_id) as? String
|
|
67
73
|
}
|
|
68
74
|
}
|
|
75
|
+
private val buttonAccessibilityDelegate =
|
|
76
|
+
object : View.AccessibilityDelegate() {
|
|
77
|
+
override fun onInitializeAccessibilityNodeInfo(
|
|
78
|
+
host: View,
|
|
79
|
+
info: android.view.accessibility.AccessibilityNodeInfo,
|
|
80
|
+
) {
|
|
81
|
+
super.onInitializeAccessibilityNodeInfo(host, info)
|
|
82
|
+
info.className = android.widget.Button::class.java.name
|
|
83
|
+
info.isEnabled = host.isEnabled
|
|
84
|
+
info.isClickable = host.isEnabled && host.isClickable
|
|
85
|
+
info.viewIdResourceName = host.getTag(com.facebook.react.R.id.react_test_id) as? String
|
|
86
|
+
}
|
|
87
|
+
}
|
|
69
88
|
|
|
70
89
|
private val trailingPaintFlags = trailingViews.map { it.paintFlags }
|
|
71
90
|
|
|
@@ -130,7 +149,12 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
130
149
|
(checkbox.layoutParams as LayoutParams).marginStart = dp(12)
|
|
131
150
|
for ((index, key) in listOf("value", "valueSecondary").withIndex()) {
|
|
132
151
|
style.optJSONObject(key)?.let { value ->
|
|
133
|
-
semanticValueViews.getOrNull(index)?.let {
|
|
152
|
+
semanticValueViews.getOrNull(index)?.let { view ->
|
|
153
|
+
applyStyledText(view, value)
|
|
154
|
+
if (value.has("fontSize")) segmentedValuePairs[view]?.let { accessory ->
|
|
155
|
+
restorePairSubscriptSizes(view, accessory, value.optDouble("fontSize").toFloat())
|
|
156
|
+
}
|
|
157
|
+
}
|
|
134
158
|
}
|
|
135
159
|
}
|
|
136
160
|
if (style.has("trailingGap")) {
|
|
@@ -146,6 +170,13 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
146
170
|
}
|
|
147
171
|
}
|
|
148
172
|
}
|
|
173
|
+
val pairedChevron = horizontal && (kinds == listOf("valuePair", "chevron") ||
|
|
174
|
+
(kinds == listOf("icon", "chevron") &&
|
|
175
|
+
descriptors.getJSONObject(0).optString("name") == "RefreshCcwOutline" &&
|
|
176
|
+
descriptors.getJSONObject(0).optString("actionKey").isEmpty()))
|
|
177
|
+
if (pairedChevron) {
|
|
178
|
+
(trailingIcons[1].layoutParams as LayoutParams).marginEnd = 0
|
|
179
|
+
}
|
|
149
180
|
visibility =
|
|
150
181
|
if ((0 until childCount).any { getChildAt(it).visibility != GONE }) VISIBLE else GONE
|
|
151
182
|
}
|
|
@@ -167,37 +198,53 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
167
198
|
|
|
168
199
|
fun anchorInset(view: View): Int =
|
|
169
200
|
if (
|
|
170
|
-
|
|
171
|
-
view
|
|
172
|
-
(
|
|
201
|
+
view in trailingIcons &&
|
|
202
|
+
(view.layoutParams as MarginLayoutParams).marginStart < 0 &&
|
|
203
|
+
((tag as? NativeListItem)?.json?.optString("presentation") == "accountSelector" ||
|
|
204
|
+
(tag as? NativeListItem)?.json?.optJSONArray("trailing")?.let { trailing ->
|
|
205
|
+
(0 until trailing.length()).any { index ->
|
|
206
|
+
trailing.optJSONObject(index)?.optString("buttonStyle") == "tertiary"
|
|
207
|
+
}
|
|
208
|
+
} == true)
|
|
173
209
|
)
|
|
174
210
|
dp(7)
|
|
175
211
|
else 0
|
|
176
212
|
|
|
177
213
|
fun reset() {
|
|
214
|
+
bindingEpoch += 1
|
|
178
215
|
tag = null
|
|
179
216
|
endInset = null
|
|
180
217
|
requestsUnclippedHost = false
|
|
181
218
|
clipChildren = true
|
|
182
219
|
clipToPadding = true
|
|
183
220
|
semanticValueViews.clear()
|
|
221
|
+
segmentedValuePairs.clear()
|
|
184
222
|
boundCheckboxData = null
|
|
185
223
|
trailingViews.forEachIndexed { index, v ->
|
|
224
|
+
buttonDownEpochs[index] = -1
|
|
186
225
|
v.paintFlags = trailingPaintFlags[index]
|
|
187
226
|
v.visibility = GONE
|
|
188
227
|
v.text = ""
|
|
189
228
|
v.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
|
190
229
|
v.setOnClickListener(null)
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// the click again only for a text accessory that carries an action.
|
|
230
|
+
// A clickable value consumes the row press; reset it until an action binds.
|
|
231
|
+
v.setOnTouchListener(null)
|
|
232
|
+
v.isPressed = false
|
|
195
233
|
v.isClickable = false
|
|
234
|
+
v.isEnabled = true
|
|
235
|
+
v.alpha = 1f
|
|
236
|
+
v.contentDescription = null
|
|
237
|
+
v.setTag(com.facebook.react.R.id.react_test_id, null)
|
|
238
|
+
v.accessibilityDelegate = null
|
|
196
239
|
v.background = null
|
|
197
240
|
v.setPadding(0, 0, 0, 0)
|
|
198
241
|
// Legacy trailing text: END gravity, one line, no ellipsis.
|
|
199
242
|
v.gravity = Gravity.END
|
|
200
243
|
v.opticalOffsetY = 0f
|
|
244
|
+
v.helpBindingGeneration += 1
|
|
245
|
+
v.guardsHelpTouches = false
|
|
246
|
+
v.primaryHelpOnly = false
|
|
247
|
+
v.showsHelpUnderline = false
|
|
201
248
|
v.maxLines = 1
|
|
202
249
|
v.setHorizontallyScrolling(false)
|
|
203
250
|
v.ellipsize = null
|
|
@@ -257,6 +304,83 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
257
304
|
for (index in 0 until minOf(2, accessories.length())) {
|
|
258
305
|
val accessory = accessories.getJSONObject(index)
|
|
259
306
|
when (accessory.optString("kind")) {
|
|
307
|
+
"button" -> {
|
|
308
|
+
if (textIndex !in trailingViews.indices) continue
|
|
309
|
+
val view = trailingViews[textIndex]
|
|
310
|
+
val buttonIndex = textIndex
|
|
311
|
+
val epoch = bindingEpoch
|
|
312
|
+
val disabled =
|
|
313
|
+
item.json.optBoolean("disabled", false) || accessory.optBoolean("disabled", false)
|
|
314
|
+
showTrailing(textIndex, accessory.optString("label"), false)
|
|
315
|
+
view.typeface = fonts.medium(context)
|
|
316
|
+
view.gravity = Gravity.CENTER
|
|
317
|
+
view.setTextColor(
|
|
318
|
+
safeColor(
|
|
319
|
+
accessory.optString("textColor").takeIf(String::isNotEmpty),
|
|
320
|
+
color(theme, "primaryText", "#000000DF"),
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
view.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, dp(30))
|
|
324
|
+
view.setPadding(dp(11), dp(4), dp(11), dp(4))
|
|
325
|
+
view.background =
|
|
326
|
+
StateListDrawable().apply {
|
|
327
|
+
fun fill(value: Int) =
|
|
328
|
+
GradientDrawable().apply {
|
|
329
|
+
setColor(value)
|
|
330
|
+
cornerRadius = dp(15).toFloat()
|
|
331
|
+
setStroke(dp(1), Color.TRANSPARENT)
|
|
332
|
+
}
|
|
333
|
+
addState(
|
|
334
|
+
intArrayOf(android.R.attr.state_pressed),
|
|
335
|
+
fill(safeColor(accessory.optString("pressedBackgroundColor").takeIf(String::isNotEmpty),
|
|
336
|
+
color(theme, "rowPressedBackground", "#00000017"))),
|
|
337
|
+
)
|
|
338
|
+
addState(
|
|
339
|
+
intArrayOf(),
|
|
340
|
+
fill(safeColor(accessory.optString("backgroundColor").takeIf(String::isNotEmpty),
|
|
341
|
+
color(theme, "strongBackground", "#0000000F"))),
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
view.setTag(
|
|
345
|
+
com.facebook.react.R.id.react_test_id,
|
|
346
|
+
accessory.optString("testID").takeIf(String::isNotEmpty),
|
|
347
|
+
)
|
|
348
|
+
view.accessibilityDelegate = buttonAccessibilityDelegate
|
|
349
|
+
view.contentDescription =
|
|
350
|
+
accessory.optString("accessibilityLabel").ifEmpty { accessory.optString("label") }
|
|
351
|
+
view.isEnabled = !disabled
|
|
352
|
+
view.alpha = if (disabled) 0.4f else 1f
|
|
353
|
+
view.isClickable = true
|
|
354
|
+
view.setOnTouchListener { _, event ->
|
|
355
|
+
when (event.actionMasked) {
|
|
356
|
+
MotionEvent.ACTION_DOWN -> {
|
|
357
|
+
buttonDownEpochs[buttonIndex] = epoch
|
|
358
|
+
false
|
|
359
|
+
}
|
|
360
|
+
MotionEvent.ACTION_UP -> {
|
|
361
|
+
if (buttonDownEpochs[buttonIndex] != bindingEpoch) {
|
|
362
|
+
view.isPressed = false
|
|
363
|
+
true
|
|
364
|
+
} else false
|
|
365
|
+
}
|
|
366
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
367
|
+
buttonDownEpochs[buttonIndex] = -1
|
|
368
|
+
false
|
|
369
|
+
}
|
|
370
|
+
else -> false
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
view.setOnClickListener {
|
|
374
|
+
if (epoch != bindingEpoch) return@setOnClickListener
|
|
375
|
+
(tag as? NativeListItem)?.takeUnless { it.json.optBoolean("disabled", false) }
|
|
376
|
+
?.takeUnless { accessory.optBoolean("disabled", false) }
|
|
377
|
+
?.let {
|
|
378
|
+
emitAction(it, accessory.optString("actionKey"), null, view,
|
|
379
|
+
"trailingAccessory", index)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
textIndex++
|
|
383
|
+
}
|
|
260
384
|
"value" -> {
|
|
261
385
|
showTrailing(
|
|
262
386
|
textIndex,
|
|
@@ -269,6 +393,21 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
269
393
|
}
|
|
270
394
|
"valuePair" -> {
|
|
271
395
|
showTrailingValuePair(textIndex, accessory, theme)
|
|
396
|
+
accessory.optJSONObject("primaryHelp")?.let { help ->
|
|
397
|
+
val view = trailingViews[textIndex]
|
|
398
|
+
val epoch = bindingEpoch
|
|
399
|
+
view.primaryHelpOnly = true
|
|
400
|
+
view.guardsHelpTouches = true
|
|
401
|
+
view.showsHelpUnderline = help.optString("underline") == "dashed"
|
|
402
|
+
if (help.optString("presentation") == "popover") {
|
|
403
|
+
view.setOnClickListener {
|
|
404
|
+
if (epoch == bindingEpoch && !item.json.optBoolean("disabled")) {
|
|
405
|
+
emitAction(item, help.optString("actionKey"), null, view, "trailingAccessory", index)
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
segmentedValuePairs[trailingViews[textIndex]] = accessory
|
|
272
411
|
semanticValueViews.add(trailingViews[textIndex++])
|
|
273
412
|
}
|
|
274
413
|
"checkbox" -> bindCheckbox(item, accessory, checkboxState)
|
|
@@ -428,8 +567,10 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
428
567
|
|
|
429
568
|
private fun showTrailingValuePair(index: Int, accessory: JSONObject, theme: JSONObject?) {
|
|
430
569
|
if (index !in trailingViews.indices) return
|
|
431
|
-
val
|
|
432
|
-
val
|
|
570
|
+
val primarySegments = accessory.optJSONArray("primaryTextSegments")
|
|
571
|
+
val secondarySegments = accessory.optJSONArray("secondaryTextSegments")
|
|
572
|
+
val primary = segmentTextOrFallback(primarySegments, accessory.optString("primary"))
|
|
573
|
+
val secondary = segmentTextOrFallback(secondarySegments, accessory.optString("secondary"))
|
|
433
574
|
if (primary.isEmpty() && secondary.isEmpty()) return
|
|
434
575
|
val value = SpannableStringBuilder()
|
|
435
576
|
val primaryStart = value.length
|
|
@@ -449,6 +590,15 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
449
590
|
value.length,
|
|
450
591
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
451
592
|
)
|
|
593
|
+
if (primary.isNotEmpty() && accessory.optString("primaryFontWeight") == "medium") {
|
|
594
|
+
value.setSpan(
|
|
595
|
+
ValuePairTypefaceSpan(fonts.medium(context)),
|
|
596
|
+
primaryStart,
|
|
597
|
+
value.length,
|
|
598
|
+
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
599
|
+
)
|
|
600
|
+
}
|
|
601
|
+
applyPairSubscriptSizes(value, primarySegments, primaryStart, 16f)
|
|
452
602
|
if (primary.isNotEmpty() && secondary.isNotEmpty()) value.append('\n')
|
|
453
603
|
val secondaryStart = value.length
|
|
454
604
|
value.append(secondary)
|
|
@@ -469,6 +619,7 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
469
619
|
value.length,
|
|
470
620
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
471
621
|
)
|
|
622
|
+
applyPairSubscriptSizes(value, secondarySegments, secondaryStart, 14f)
|
|
472
623
|
val view = trailingViews[index]
|
|
473
624
|
view.text = value
|
|
474
625
|
view.maxLines = 2
|
|
@@ -480,6 +631,48 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
480
631
|
view.visibility = VISIBLE
|
|
481
632
|
}
|
|
482
633
|
|
|
634
|
+
private fun segmentTextOrFallback(segments: JSONArray?, fallback: String): String {
|
|
635
|
+
if (segments == null || segments.length() == 0) return fallback
|
|
636
|
+
return buildString {
|
|
637
|
+
for (index in 0 until segments.length()) append(segments.getJSONObject(index).optString("text"))
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
private fun applyPairSubscriptSizes(
|
|
642
|
+
value: SpannableStringBuilder,
|
|
643
|
+
segments: JSONArray?,
|
|
644
|
+
start: Int,
|
|
645
|
+
baseSize: Float,
|
|
646
|
+
) {
|
|
647
|
+
if (segments == null) return
|
|
648
|
+
var offset = start
|
|
649
|
+
for (index in 0 until segments.length()) {
|
|
650
|
+
val segment = segments.getJSONObject(index)
|
|
651
|
+
val end = offset + segment.optString("text").length
|
|
652
|
+
if (end > offset && segment.optString("style") == "subscript")
|
|
653
|
+
value.setSpan(
|
|
654
|
+
AbsoluteSizeSpan(sp(kotlin.math.ceil(baseSize * 0.6).toFloat()).roundToInt(), true),
|
|
655
|
+
offset,
|
|
656
|
+
end,
|
|
657
|
+
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
658
|
+
)
|
|
659
|
+
offset = end
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
private fun restorePairSubscriptSizes(view: TextView, accessory: JSONObject, baseSize: Float) {
|
|
664
|
+
val primary = accessory.optJSONArray("primaryTextSegments")
|
|
665
|
+
val secondary = accessory.optJSONArray("secondaryTextSegments")
|
|
666
|
+
if ((primary?.length() ?: 0) == 0 && (secondary?.length() ?: 0) == 0) return
|
|
667
|
+
val value = SpannableStringBuilder(view.text)
|
|
668
|
+
applyPairSubscriptSizes(value, primary, 0, baseSize)
|
|
669
|
+
val primaryText = segmentTextOrFallback(primary, accessory.optString("primary"))
|
|
670
|
+
val secondaryStart = primaryText.length + if (primaryText.isNotEmpty() &&
|
|
671
|
+
segmentTextOrFallback(secondary, accessory.optString("secondary")).isNotEmpty()) 1 else 0
|
|
672
|
+
applyPairSubscriptSizes(value, secondary, secondaryStart, baseSize)
|
|
673
|
+
view.text = value
|
|
674
|
+
}
|
|
675
|
+
|
|
483
676
|
private fun showTrailingMenu(index: Int, actionKey: String) {
|
|
484
677
|
showTrailing(index, "⋮", true, actionKey)
|
|
485
678
|
if (index !in trailingViews.indices) return
|
|
@@ -519,7 +712,26 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
519
712
|
)
|
|
520
713
|
icon.accessibilityDelegate = selectorAccessibilityDelegate
|
|
521
714
|
icon.contentDescription = data.optString("accessibilityLabel").takeIf { it.isNotEmpty() }
|
|
522
|
-
if (
|
|
715
|
+
if (data.optString("buttonStyle") == "tertiary") {
|
|
716
|
+
icon.glyphSizeDp = 24
|
|
717
|
+
icon.layoutParams = LayoutParams(dp(38), dp(38)).apply {
|
|
718
|
+
gravity = Gravity.CENTER_VERTICAL
|
|
719
|
+
marginStart = -dp(7)
|
|
720
|
+
marginEnd = -dp(7)
|
|
721
|
+
}
|
|
722
|
+
icon.background = StateListDrawable().apply {
|
|
723
|
+
addState(intArrayOf(android.R.attr.state_pressed),
|
|
724
|
+
roundedFill(color(theme, "rowPressedBackground", "#00000017"), 19f).apply {
|
|
725
|
+
setStroke(dp(1), Color.TRANSPARENT)
|
|
726
|
+
})
|
|
727
|
+
addState(intArrayOf(), roundedFill(Color.TRANSPARENT, 19f).apply {
|
|
728
|
+
setStroke(dp(1), Color.TRANSPARENT)
|
|
729
|
+
})
|
|
730
|
+
}
|
|
731
|
+
clipChildren = false
|
|
732
|
+
clipToPadding = false
|
|
733
|
+
requestsUnclippedHost = true
|
|
734
|
+
} else if (item.json.optString("presentation") == "accountSelector") {
|
|
523
735
|
icon.glyphSizeDp = 24
|
|
524
736
|
val isSourceMenu = item.hasExplicitHeight && icon.iconName == "DotHorOutline"
|
|
525
737
|
val size = if (item.hasExplicitHeight && icon.iconName == "PlusSmallOutline") 36 else 38
|
|
@@ -545,6 +757,13 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
545
757
|
addState(intArrayOf(), roundedFill(Color.TRANSPARENT, 19f))
|
|
546
758
|
}
|
|
547
759
|
}
|
|
760
|
+
} else if (icon.iconName == "RefreshCcwOutline" && data.optString("actionKey").isEmpty()) {
|
|
761
|
+
icon.glyphSizeDp = 16
|
|
762
|
+
icon.isClickable = false
|
|
763
|
+
icon.isFocusable = false
|
|
764
|
+
icon.layoutParams = LayoutParams(dp(16), dp(16)).apply {
|
|
765
|
+
gravity = Gravity.CENTER_VERTICAL
|
|
766
|
+
}
|
|
548
767
|
} else if (icon.iconName == "ChevronRightSmallOutline") {
|
|
549
768
|
icon.glyphSizeDp = null
|
|
550
769
|
// ListItem.DrillIn is a 24dp icon with mx=-6, for a 12dp layout footprint.
|
|
@@ -716,3 +935,13 @@ internal class NativeListAccessoryStack(context: android.content.Context, val fo
|
|
|
716
935
|
cornerRadius = scaledDp(radiusDp)
|
|
717
936
|
}
|
|
718
937
|
}
|
|
938
|
+
|
|
939
|
+
private class ValuePairTypefaceSpan(private val face: Typeface) : MetricAffectingSpan() {
|
|
940
|
+
override fun updateDrawState(paint: TextPaint) {
|
|
941
|
+
paint.typeface = face
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
override fun updateMeasureState(paint: TextPaint) {
|
|
945
|
+
paint.typeface = face
|
|
946
|
+
}
|
|
947
|
+
}
|
|
@@ -70,10 +70,13 @@ internal class NativeListActivityDetailsView(private val reactContext: ThemedRea
|
|
|
70
70
|
val style = data.optJSONObject("style") ?: JSONObject()
|
|
71
71
|
val image = style.optJSONObject("image") ?: JSONObject()
|
|
72
72
|
val table = data.optString("presentation") == "table"
|
|
73
|
+
val hideLeading = data.optBoolean("hideLeading")
|
|
73
74
|
val gap = if (style.has("leadingGap")) px(style.optDouble("leadingGap")) else dp(12)
|
|
74
75
|
val iw = if (image.has("width")) px(image.optDouble("width")) else dp(40)
|
|
75
|
-
visual.layoutParams = LayoutParams(iw, if (image.has("height")) px(image.optDouble("height")) else dp(40)).apply { marginEnd = gap }
|
|
76
|
-
visual.
|
|
76
|
+
visual.layoutParams = LayoutParams(iw, if (image.has("height")) px(image.optDouble("height")) else dp(40)).apply { marginEnd = if (hideLeading) 0 else gap }
|
|
77
|
+
visual.visibility = if (hideLeading) GONE else VISIBLE
|
|
78
|
+
if (hideLeading) visual.recycle()
|
|
79
|
+
else visual.bind(data.optJSONObject("leading") ?: JSONObject(), image, item.key, theme, false, sourceScale, data.optJSONObject("secondaryLeading"))
|
|
77
80
|
amounts.layoutParams = if (table) LayoutParams(0, -2, 1f).apply { marginStart = gap } else LayoutParams(-2, -2).apply { marginStart = gap }
|
|
78
81
|
text(title, data.optString("title"), style.optJSONObject("title"), 16f, color("primary", theme), 24, "medium")
|
|
79
82
|
text(description, data.optString("description"), style.optJSONObject("description"), 14f, color("secondary", theme), 20, lines = 2)
|
|
@@ -117,7 +120,7 @@ internal class NativeListActivityDetailsView(private val reactContext: ThemedRea
|
|
|
117
120
|
actions.removeAllViews()
|
|
118
121
|
val actionData = data.optJSONArray("footerActions")
|
|
119
122
|
actions.visibility = if ((actionData?.length() ?: 0) == 0) GONE else VISIBLE
|
|
120
|
-
actions.layoutParams = LayoutParams(-1, -2).apply { marginStart = iw + gap; topMargin = dp(8) }
|
|
123
|
+
actions.layoutParams = LayoutParams(-1, -2).apply { marginStart = if (hideLeading) 0 else iw + gap; topMargin = dp(8) }
|
|
121
124
|
for (index in 0 until (actionData?.length() ?: 0)) {
|
|
122
125
|
val descriptor = actionData!!.getJSONObject(index)
|
|
123
126
|
val button = NativeListTextView(context)
|
|
@@ -146,7 +149,7 @@ internal class NativeListActivityDetailsView(private val reactContext: ThemedRea
|
|
|
146
149
|
}
|
|
147
150
|
label.text = styled
|
|
148
151
|
}
|
|
149
|
-
fun recycle() { visual.recycle(); amountViews.forEach { it.recycle() }; description.setOnClickListener(null); actions.removeAllViews() }
|
|
152
|
+
fun recycle() { visual.recycle(); visual.visibility = VISIBLE; amountViews.forEach { it.recycle() }; description.setOnClickListener(null); actions.removeAllViews() }
|
|
150
153
|
fun dispose() { recycle(); visual.dispose(); amountViews.forEach { it.dispose() } }
|
|
151
154
|
|
|
152
155
|
private inner class AmountView : LinearLayout(context) {
|
|
@@ -81,6 +81,7 @@ internal class NativeListActivityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
81
81
|
details.recycle()
|
|
82
82
|
val style = data.optJSONObject("style") ?: JSONObject()
|
|
83
83
|
val image = style.optJSONObject("image") ?: JSONObject()
|
|
84
|
+
val hideLeading = data.optBoolean("hideLeading")
|
|
84
85
|
fun color(name: String, fallback: String) =
|
|
85
86
|
parseNativeListColor(theme?.optString(name, fallback) ?: fallback)
|
|
86
87
|
fun fill(value: Int, radius: Int) =
|
|
@@ -143,8 +144,10 @@ internal class NativeListActivityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
143
144
|
val iw = if (image.has("width")) stylePx(image.optDouble("width")) else dp(40)
|
|
144
145
|
val ih = if (image.has("height")) stylePx(image.optDouble("height")) else dp(40)
|
|
145
146
|
val gap = if (style.has("leadingGap")) stylePx(style.optDouble("leadingGap")) else dp(12)
|
|
146
|
-
visual.layoutParams = LayoutParams(iw, ih).apply { marginEnd = gap }
|
|
147
|
-
visual.
|
|
147
|
+
visual.layoutParams = LayoutParams(iw, ih).apply { marginEnd = if (hideLeading) 0 else gap }
|
|
148
|
+
visual.visibility = if (hideLeading) GONE else VISIBLE
|
|
149
|
+
if (hideLeading) visual.recycle()
|
|
150
|
+
else visual.bind(
|
|
148
151
|
// Legacy: a missing visual still reserves an empty, transparent slot.
|
|
149
152
|
data.optJSONObject("leading") ?: JSONObject().put("backgroundColor", "#00000000"),
|
|
150
153
|
image,
|
|
@@ -212,7 +215,7 @@ internal class NativeListActivityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
212
215
|
actions.visibility = if (count == 0) GONE else VISIBLE
|
|
213
216
|
actions.layoutParams =
|
|
214
217
|
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
|
215
|
-
marginStart = if (style.has("leadingGap") || image.has("width")) iw + gap else dp(52)
|
|
218
|
+
marginStart = if (hideLeading) 0 else if (style.has("leadingGap") || image.has("width")) iw + gap else dp(52)
|
|
216
219
|
topMargin = if (style.has("lineGap")) stylePx(style.optDouble("lineGap")) else dp(8)
|
|
217
220
|
bottomMargin = dp(4)
|
|
218
221
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package com.margelo.nitro.nativelist
|
|
2
|
+
|
|
3
|
+
import android.content.res.ColorStateList
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.graphics.drawable.GradientDrawable
|
|
6
|
+
import android.graphics.drawable.StateListDrawable
|
|
7
|
+
import android.view.Gravity
|
|
8
|
+
import android.view.View
|
|
9
|
+
import android.widget.FrameLayout
|
|
10
|
+
import android.widget.LinearLayout
|
|
11
|
+
import android.widget.ProgressBar
|
|
12
|
+
import androidx.core.widget.TextViewCompat
|
|
13
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
14
|
+
import org.json.JSONObject
|
|
15
|
+
|
|
16
|
+
internal class NativeListButtonGroupRowView(context: ThemedReactContext, fonts: NativeListFonts) :
|
|
17
|
+
NativeListRendererRowView(context, fonts) {
|
|
18
|
+
private var generation = 0
|
|
19
|
+
private var rowOpacity = 1f
|
|
20
|
+
|
|
21
|
+
override fun appearance() {
|
|
22
|
+
super.appearance()
|
|
23
|
+
alpha = rowOpacity
|
|
24
|
+
}
|
|
25
|
+
override fun defaultHeight(item: NativeListItem, layout: String) = 64
|
|
26
|
+
|
|
27
|
+
override fun bindContent(
|
|
28
|
+
item: NativeListItem,
|
|
29
|
+
theme: JSONObject?,
|
|
30
|
+
layout: String,
|
|
31
|
+
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
|
|
32
|
+
) {
|
|
33
|
+
recycleContent()
|
|
34
|
+
rowOpacity = (item.json.optJSONObject("style")?.optJSONObject("container")?.optDouble("opacity", item.json.optDouble("opacity", 1.0)) ?: item.json.optDouble("opacity", 1.0)).toFloat()
|
|
35
|
+
orientation = HORIZONTAL
|
|
36
|
+
gravity = Gravity.CENTER_VERTICAL
|
|
37
|
+
setPadding(0, dp(8), 0, dp(8))
|
|
38
|
+
val info = parseNativeListColor(theme?.optString("info", "#0F69F6") ?: "#0F69F6")
|
|
39
|
+
val descriptors = item.json.optJSONArray("buttons") ?: return
|
|
40
|
+
val epoch = generation
|
|
41
|
+
for (index in 0 until descriptors.length()) {
|
|
42
|
+
val descriptor = descriptors.getJSONObject(index)
|
|
43
|
+
val enabled = !item.json.optBoolean("disabled") && !descriptor.optBoolean("disabled") && !descriptor.optBoolean("loading")
|
|
44
|
+
val button = FrameLayout(context).apply {
|
|
45
|
+
isEnabled = enabled
|
|
46
|
+
alpha = if (enabled) 1f else 0.4f
|
|
47
|
+
contentDescription = descriptor.optString("accessibilityLabel", descriptor.optString("label"))
|
|
48
|
+
setTag(com.facebook.react.R.id.react_test_id, descriptor.optString("testID").takeIf { it.isNotEmpty() })
|
|
49
|
+
background = StateListDrawable().apply {
|
|
50
|
+
fun fill(pressed: Boolean) = GradientDrawable().apply {
|
|
51
|
+
setColor(if (pressed) (info and 0x00FFFFFF) or (43 shl 24) else Color.TRANSPARENT)
|
|
52
|
+
cornerRadius = dp(12).toFloat()
|
|
53
|
+
setStroke(dp(1), (info and 0x00FFFFFF) or (89 shl 24))
|
|
54
|
+
}
|
|
55
|
+
addState(intArrayOf(android.R.attr.state_pressed), fill(true))
|
|
56
|
+
addState(intArrayOf(), fill(false))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
val label = NativeListTextView(context).apply {
|
|
60
|
+
text = descriptor.optString("label")
|
|
61
|
+
textSize = NativeListScale.font(resources, 14f)
|
|
62
|
+
typeface = fonts.medium(context)
|
|
63
|
+
setTextColor(info)
|
|
64
|
+
gravity = Gravity.CENTER
|
|
65
|
+
includeFontPadding = false
|
|
66
|
+
maxLines = 1
|
|
67
|
+
ellipsize = android.text.TextUtils.TruncateAt.END
|
|
68
|
+
TextViewCompat.setLineHeight(this, dp(20))
|
|
69
|
+
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
|
70
|
+
}
|
|
71
|
+
val content = LinearLayout(context).apply {
|
|
72
|
+
orientation = HORIZONTAL
|
|
73
|
+
gravity = Gravity.CENTER
|
|
74
|
+
setPadding(dp(8), dp(12), dp(8), dp(12))
|
|
75
|
+
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
|
76
|
+
}
|
|
77
|
+
button.addView(content, FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
|
|
78
|
+
if (descriptor.optBoolean("loading")) {
|
|
79
|
+
val spinner = ProgressBar(context, null, android.R.attr.progressBarStyleSmall).apply {
|
|
80
|
+
isIndeterminate = true
|
|
81
|
+
indeterminateTintList = ColorStateList.valueOf(info)
|
|
82
|
+
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
|
83
|
+
}
|
|
84
|
+
content.addView(spinner, LayoutParams(dp(20), dp(20)).apply { marginEnd = dp(8) })
|
|
85
|
+
}
|
|
86
|
+
content.addView(label, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
|
|
87
|
+
button.setOnClickListener {
|
|
88
|
+
if (generation == epoch && enabled) emitAction(descriptor.optString("actionKey"), button, "buttonGroup", index)
|
|
89
|
+
}
|
|
90
|
+
button.isClickable = enabled
|
|
91
|
+
addView(button, LayoutParams(0, dp(48), 1f).apply { if (index > 0) marginStart = dp(10) })
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
override fun recycleContent() {
|
|
96
|
+
generation += 1
|
|
97
|
+
for (index in 0 until childCount) getChildAt(index).setOnClickListener(null)
|
|
98
|
+
removeAllViews()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
override fun disposeContent() = recycleContent()
|
|
102
|
+
}
|
|
@@ -11,6 +11,15 @@ internal class NativeListContainerSlotAdapter : RecyclerView.Adapter<NativeListC
|
|
|
11
11
|
private var content: View? = null
|
|
12
12
|
private var height = 0
|
|
13
13
|
private var enabled = true
|
|
14
|
+
private var boundHolder: Holder? = null
|
|
15
|
+
private var visualTranslationY = 0f
|
|
16
|
+
|
|
17
|
+
fun contentView(): View? = content
|
|
18
|
+
|
|
19
|
+
fun setVisualTranslationY(value: Float) {
|
|
20
|
+
visualTranslationY = value
|
|
21
|
+
boundHolder?.container?.translationY = value
|
|
22
|
+
}
|
|
14
23
|
|
|
15
24
|
fun update(view: View?, heightPx: Int, visible: Boolean) {
|
|
16
25
|
val previousCount = itemCount
|
|
@@ -39,6 +48,8 @@ internal class NativeListContainerSlotAdapter : RecyclerView.Adapter<NativeListC
|
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
override fun onBindViewHolder(holder: Holder, position: Int) {
|
|
51
|
+
boundHolder = holder
|
|
52
|
+
holder.container.translationY = visualTranslationY
|
|
42
53
|
holder.container.layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height)
|
|
43
54
|
holder.container.removeAllViews()
|
|
44
55
|
content?.let { view ->
|
|
@@ -48,6 +59,7 @@ internal class NativeListContainerSlotAdapter : RecyclerView.Adapter<NativeListC
|
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
override fun onViewRecycled(holder: Holder) {
|
|
62
|
+
if (boundHolder === holder) boundHolder = null
|
|
51
63
|
holder.container.removeAllViews()
|
|
52
64
|
}
|
|
53
65
|
}
|