react-native-list 1.0.0 → 2.0.0-alpha.1
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 +190 -32
- package/ReactNativeList.podspec +39 -0
- package/android/CMakeLists.txt +48 -0
- package/android/build.gradle +151 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/JHybridUiListModule.cpp +192 -0
- package/android/src/main/cpp/JHybridUiListModule.h +50 -0
- package/android/src/main/cpp/cpp-adapter.cpp +12 -0
- package/android/src/main/java/com/hannojg/reactnativelist/ReactNativeListPackage.kt +27 -0
- package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridNativeListDataSource.kt +146 -0
- package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridNativeListLayout.kt +86 -0
- package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridUiListModule.kt +116 -0
- package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridUiListView.kt +410 -0
- package/android/src/main/java/com/margelo/nitro/reactnativelist/HybridViewHolder.kt +9 -0
- package/android/src/main/java/com/margelo/nitro/reactnativelist/NativeListAdapter.kt +217 -0
- package/ios/DataSource/HybridNativeListDataSource.swift +213 -0
- package/ios/HybridObjects/HybridUiListModule.swift +49 -0
- package/ios/HybridObjects/HybridViewHolder.swift +16 -0
- package/ios/Layout/HybridNativeListLayout.swift +128 -0
- package/ios/Utils/ErrorUtils.h +26 -0
- package/ios/Utils/HybridIOSWorkletsModuleProxyHolder.swift +10 -0
- package/ios/Utils/SurfaceHelper.h +20 -0
- package/ios/Utils/SurfaceHelper.mm +144 -0
- package/ios/Utils/SurfacePresenterRegistry.h +17 -0
- package/ios/Utils/SurfacePresenterRegistry.m +31 -0
- package/ios/Utils/TurboModuleInstaller.h +18 -0
- package/ios/Utils/TurboModuleInstaller.mm +267 -0
- package/ios/Views/HostCell.swift +216 -0
- package/ios/Views/HybridUiListView.swift +695 -0
- package/metro/RendererProxyThreadSwitch.js +66 -0
- package/metro-config.d.ts +1 -0
- package/metro-config.js +52 -0
- package/nitro.json +47 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/ReactNativeList+autolinking.cmake +99 -0
- package/nitrogen/generated/android/ReactNativeList+autolinking.gradle +27 -0
- package/nitrogen/generated/android/ReactNativeListOnLoad.cpp +156 -0
- package/nitrogen/generated/android/ReactNativeListOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JFunc_bool_NativeListItem_NativeListItem.hpp +83 -0
- package/nitrogen/generated/android/c++/JFunc_bool_double_NativeListItem_double.hpp +83 -0
- package/nitrogen/generated/android/c++/JFunc_double_std__string.hpp +78 -0
- package/nitrogen/generated/android/c++/JHybridIOSWorkletsModuleProxyHolderSpec.cpp +49 -0
- package/nitrogen/generated/android/c++/JHybridIOSWorkletsModuleProxyHolderSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JHybridNativeLinearListLayoutSpec.cpp +63 -0
- package/nitrogen/generated/android/c++/JHybridNativeLinearListLayoutSpec.hpp +65 -0
- package/nitrogen/generated/android/c++/JHybridNativeListDataSourceSpec.cpp +101 -0
- package/nitrogen/generated/android/c++/JHybridNativeListDataSourceSpec.hpp +70 -0
- package/nitrogen/generated/android/c++/JHybridNativeListLayoutSpec.cpp +49 -0
- package/nitrogen/generated/android/c++/JHybridNativeListLayoutSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JHybridUiListModuleSpec.cpp +65 -0
- package/nitrogen/generated/android/c++/JHybridUiListModuleSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JHybridUiListViewSpec.cpp +92 -0
- package/nitrogen/generated/android/c++/JHybridUiListViewSpec.hpp +67 -0
- package/nitrogen/generated/android/c++/JHybridViewHolderSpec.cpp +49 -0
- package/nitrogen/generated/android/c++/JHybridViewHolderSpec.hpp +63 -0
- package/nitrogen/generated/android/c++/JNativeItemSizeEstimate.hpp +61 -0
- package/nitrogen/generated/android/c++/JNativeLinearListLayoutConfig.hpp +81 -0
- package/nitrogen/generated/android/c++/JNativeLinearListLayoutIOSConfig.hpp +59 -0
- package/nitrogen/generated/android/c++/JNativeListItem.hpp +76 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_HybridIOSWorkletsModuleProxyHolderSpec.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_HybridIOSWorkletsModuleProxyHolderSpec.hpp +72 -0
- package/nitrogen/generated/android/c++/views/JHybridUiListViewStateUpdater.cpp +53 -0
- package/nitrogen/generated/android/c++/views/JHybridUiListViewStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/c++/views/JHybridViewHolderStateUpdater.cpp +53 -0
- package/nitrogen/generated/android/c++/views/JHybridViewHolderStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Func_bool_NativeListItem_NativeListItem.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Func_bool_double_NativeListItem_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Func_double_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridIOSWorkletsModuleProxyHolderSpec.kt +52 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridNativeLinearListLayoutSpec.kt +54 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridNativeListDataSourceSpec.kt +87 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridNativeListLayoutSpec.kt +52 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridUiListModuleSpec.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridUiListViewSpec.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/HybridViewHolderSpec.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeItemSizeEstimate.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeLinearListLayoutConfig.kt +76 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeLinearListLayoutIOSConfig.kt +51 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/NativeListItem.kt +71 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/ReactNativeListOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/Variant_NullType_HybridIOSWorkletsModuleProxyHolderSpec.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridUiListViewManager.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridUiListViewStateUpdater.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridViewHolderManager.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativelist/views/HybridViewHolderStateUpdater.kt +23 -0
- package/nitrogen/generated/ios/ReactNativeList+autolinking.rb +62 -0
- package/nitrogen/generated/ios/ReactNativeList-Swift-Cxx-Bridge.cpp +162 -0
- package/nitrogen/generated/ios/ReactNativeList-Swift-Cxx-Bridge.hpp +368 -0
- package/nitrogen/generated/ios/ReactNativeList-Swift-Cxx-Umbrella.hpp +92 -0
- package/nitrogen/generated/ios/ReactNativeListAutolinking.mm +83 -0
- package/nitrogen/generated/ios/ReactNativeListAutolinking.swift +86 -0
- package/nitrogen/generated/ios/c++/HybridIOSWorkletsModuleProxyHolderSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridIOSWorkletsModuleProxyHolderSpecSwift.hpp +75 -0
- package/nitrogen/generated/ios/c++/HybridNativeLinearListLayoutSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeLinearListLayoutSpecSwift.hpp +92 -0
- package/nitrogen/generated/ios/c++/HybridNativeListDataSourceSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeListDataSourceSpecSwift.hpp +132 -0
- package/nitrogen/generated/ios/c++/HybridNativeListLayoutSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeListLayoutSpecSwift.hpp +75 -0
- package/nitrogen/generated/ios/c++/HybridUiListModuleSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridUiListModuleSpecSwift.hpp +93 -0
- package/nitrogen/generated/ios/c++/HybridUiListViewSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridUiListViewSpecSwift.hpp +121 -0
- package/nitrogen/generated/ios/c++/HybridViewHolderSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridViewHolderSpecSwift.hpp +75 -0
- package/nitrogen/generated/ios/c++/views/HybridUiListViewComponent.mm +118 -0
- package/nitrogen/generated/ios/c++/views/HybridViewHolderComponent.mm +118 -0
- package/nitrogen/generated/ios/swift/Func_bool_NativeListItem_NativeListItem.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_bool_double_NativeListItem_double.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_double_std__string.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridIOSWorkletsModuleProxyHolderSpec.swift +55 -0
- package/nitrogen/generated/ios/swift/HybridIOSWorkletsModuleProxyHolderSpec_cxx.swift +128 -0
- package/nitrogen/generated/ios/swift/HybridNativeLinearListLayoutSpec.swift +55 -0
- package/nitrogen/generated/ios/swift/HybridNativeLinearListLayoutSpec_cxx.swift +140 -0
- package/nitrogen/generated/ios/swift/HybridNativeListDataSourceSpec.swift +62 -0
- package/nitrogen/generated/ios/swift/HybridNativeListDataSourceSpec_cxx.swift +222 -0
- package/nitrogen/generated/ios/swift/HybridNativeListLayoutSpec.swift +55 -0
- package/nitrogen/generated/ios/swift/HybridNativeListLayoutSpec_cxx.swift +128 -0
- package/nitrogen/generated/ios/swift/HybridUiListModuleSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridUiListModuleSpec_cxx.swift +175 -0
- package/nitrogen/generated/ios/swift/HybridUiListViewSpec.swift +59 -0
- package/nitrogen/generated/ios/swift/HybridUiListViewSpec_cxx.swift +227 -0
- package/nitrogen/generated/ios/swift/HybridViewHolderSpec.swift +55 -0
- package/nitrogen/generated/ios/swift/HybridViewHolderSpec_cxx.swift +147 -0
- package/nitrogen/generated/ios/swift/NativeItemSizeEstimate.swift +60 -0
- package/nitrogen/generated/ios/swift/NativeLinearListLayoutConfig.swift +60 -0
- package/nitrogen/generated/ios/swift/NativeLinearListLayoutIOSConfig.swift +35 -0
- package/nitrogen/generated/ios/swift/NativeListItem.swift +75 -0
- package/nitrogen/generated/ios/swift/Variant_NullType__any_HybridIOSWorkletsModuleProxyHolderSpec_.swift +30 -0
- package/nitrogen/generated/shared/c++/HybridIOSWorkletsModuleProxyHolderSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridIOSWorkletsModuleProxyHolderSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridNativeLinearListLayoutSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridNativeLinearListLayoutSpec.hpp +67 -0
- package/nitrogen/generated/shared/c++/HybridNativeListDataSourceSpec.cpp +28 -0
- package/nitrogen/generated/shared/c++/HybridNativeListDataSourceSpec.hpp +72 -0
- package/nitrogen/generated/shared/c++/HybridNativeListLayoutSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNativeListLayoutSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridUiListModuleSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridUiListModuleSpec.hpp +68 -0
- package/nitrogen/generated/shared/c++/HybridUiListViewSpec.cpp +25 -0
- package/nitrogen/generated/shared/c++/HybridUiListViewSpec.hpp +79 -0
- package/nitrogen/generated/shared/c++/HybridUiManagerHelperSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridUiManagerHelperSpec.hpp +65 -0
- package/nitrogen/generated/shared/c++/HybridViewHolderSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridViewHolderSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/NativeItemSizeEstimate.hpp +87 -0
- package/nitrogen/generated/shared/c++/NativeLinearListLayoutConfig.hpp +105 -0
- package/nitrogen/generated/shared/c++/NativeLinearListLayoutIOSConfig.hpp +85 -0
- package/nitrogen/generated/shared/c++/NativeListItem.hpp +101 -0
- package/nitrogen/generated/shared/c++/views/HybridUiListViewComponent.cpp +72 -0
- package/nitrogen/generated/shared/c++/views/HybridUiListViewComponent.hpp +109 -0
- package/nitrogen/generated/shared/c++/views/HybridViewHolderComponent.cpp +72 -0
- package/nitrogen/generated/shared/c++/views/HybridViewHolderComponent.hpp +109 -0
- package/nitrogen/generated/shared/json/UiListViewConfig.json +9 -0
- package/nitrogen/generated/shared/json/ViewHolderConfig.json +9 -0
- package/package.json +152 -5
- package/react-native.config.js +16 -0
- package/src/ListDataSource.ts +232 -0
- package/src/ListLayout.ts +95 -0
- package/src/UiListModule.ts +5 -0
- package/src/hooks/useChangeEffect.ts +50 -0
- package/src/index.tsx +49 -0
- package/src/privateGlobals.ts +20 -0
- package/src/renderer/fabric/RenderHelper.ts +29 -0
- package/src/renderer/fabric/UiManagerHelper.ts +5 -0
- package/src/renderer/react/ReactFabricMirror.bundle.js +1984 -0
- package/src/renderer/react/ReactFabricMirror.ts +766 -0
- package/src/renderer/react/ReactFabricRenderer.ts +11 -0
- package/src/specs/IOSWorkletsModuleProxyHolder.nitro.ts +6 -0
- package/src/specs/NativeLinearListLayout.nitro.ts +23 -0
- package/src/specs/NativeListDataSource.nitro.ts +28 -0
- package/src/specs/NativeListLayout.nitro.ts +6 -0
- package/src/specs/UIListModule.nitro.ts +13 -0
- package/src/specs/UIManagerHelper.nitro.ts +34 -0
- package/src/specs/UiListView.nitro.ts +31 -0
- package/src/specs/ViewHolder.nitro.ts +11 -0
- package/src/views/List.tsx +525 -0
- package/src/views/UiListHostComponent.ts +8 -0
- package/FillRateHelper.js +0 -179
- package/FlatList.js +0 -494
- package/LICENSE.md +0 -31
- package/ListView/__mocks__/ListViewMock.js +0 -55
- package/MetroListView.js +0 -166
- package/SectionList.js +0 -291
- package/ViewabilityHelper.js +0 -260
- package/VirtualizeUtils.js +0 -163
- package/VirtualizedList.js +0 -861
- package/VirtualizedSectionList.js +0 -397
- package/__flowtests__/FlatList-flowtest.js +0 -90
- package/__flowtests__/SectionList-flowtest.js +0 -89
- package/__tests__/FillRateHelper-test.js +0 -106
- package/__tests__/FlatList-test.js +0 -64
- package/__tests__/SectionList-test.js +0 -67
- package/__tests__/ViewabilityHelper-test.js +0 -359
- package/__tests__/VirtualizeUtils-test.js +0 -74
- package/__tests__/__snapshots__/FlatList-test.js.snap +0 -251
- package/__tests__/__snapshots__/SectionList-test.js.snap +0 -283
- package/index.js +0 -5
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativelist
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.os.Looper
|
|
6
|
+
import android.view.View
|
|
7
|
+
import android.view.ViewGroup
|
|
8
|
+
import androidx.recyclerview.widget.DiffUtil
|
|
9
|
+
import androidx.recyclerview.widget.LinearLayoutManager
|
|
10
|
+
import androidx.recyclerview.widget.RecyclerView
|
|
11
|
+
import com.facebook.react.ReactActivity
|
|
12
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
|
+
import com.facebook.react.interfaces.fabric.ReactSurface
|
|
14
|
+
import com.facebook.react.runtime.ReactSurfaceView
|
|
15
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
16
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
17
|
+
import com.facebook.react.uimanager.common.UIManagerType
|
|
18
|
+
import com.margelo.nitro.NitroModules
|
|
19
|
+
import java.util.concurrent.CountDownLatch
|
|
20
|
+
import java.util.concurrent.atomic.AtomicReference
|
|
21
|
+
|
|
22
|
+
typealias CreateViewCallbackType = (
|
|
23
|
+
type: String
|
|
24
|
+
) -> Double
|
|
25
|
+
typealias UpdateViewCallbackType = (
|
|
26
|
+
reactTag: Double,
|
|
27
|
+
item: NativeListItem,
|
|
28
|
+
index: Double
|
|
29
|
+
) -> Boolean
|
|
30
|
+
|
|
31
|
+
class HybridUiListView(val reactContext: ThemedReactContext) :
|
|
32
|
+
HybridUiListViewSpec(),
|
|
33
|
+
NativeListDataSourceObserver {
|
|
34
|
+
private var createViewCallback: CreateViewCallbackType? = null
|
|
35
|
+
private var updateViewCallback: UpdateViewCallbackType? = null
|
|
36
|
+
private var adapter: NativeListAdapter? = null
|
|
37
|
+
private var dataSource: HybridNativeListDataSource? = null
|
|
38
|
+
private var isRecyclerViewLayoutScheduled = false
|
|
39
|
+
private var itemContentInsets = ItemContentInsets(horizontal = 0, vertical = 0)
|
|
40
|
+
private var rendererSurface: ReactSurface? = null
|
|
41
|
+
private var rendererSurfaceId: Int? = null
|
|
42
|
+
private var isRendererSurfaceStarted = false
|
|
43
|
+
private var isDisposed = false
|
|
44
|
+
|
|
45
|
+
override val view: RecyclerView by lazy {
|
|
46
|
+
ClippedRecyclerView(reactContext).apply {
|
|
47
|
+
layoutParams = ViewGroup.LayoutParams(
|
|
48
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
49
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
50
|
+
)
|
|
51
|
+
layoutManager = LinearLayoutManager(reactContext)
|
|
52
|
+
setBackgroundColor(Color.TRANSPARENT)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun setListCallbacks(
|
|
57
|
+
uiListModule: HybridUiListModuleSpec,
|
|
58
|
+
createView: CreateViewCallbackType,
|
|
59
|
+
updateView: UpdateViewCallbackType
|
|
60
|
+
) {
|
|
61
|
+
isDisposed = false
|
|
62
|
+
createViewCallback = createView
|
|
63
|
+
updateViewCallback = updateView
|
|
64
|
+
runOnMain {
|
|
65
|
+
ensureAdapter()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override fun getSurfaceId(): Double {
|
|
70
|
+
val surfaceId = runOnMainSync {
|
|
71
|
+
ensureRendererSurface()
|
|
72
|
+
}
|
|
73
|
+
return surfaceId.toDouble()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
override fun disposeRendererSurface() {
|
|
77
|
+
runOnMainSync {
|
|
78
|
+
isDisposed = true
|
|
79
|
+
createViewCallback = null
|
|
80
|
+
updateViewCallback = null
|
|
81
|
+
dataSource?.observer = null
|
|
82
|
+
dataSource = null
|
|
83
|
+
adapter?.dataSource = null
|
|
84
|
+
adapter = null
|
|
85
|
+
// setAdapter(null) asks RecyclerView to recycle attached holders immediately.
|
|
86
|
+
// During list teardown the whole native view is going away, so drop the adapter
|
|
87
|
+
// without entering RecyclerView's recycling path.
|
|
88
|
+
view.swapAdapter(null, false)
|
|
89
|
+
view.layoutManager?.removeAllViews()
|
|
90
|
+
isRecyclerViewLayoutScheduled = false
|
|
91
|
+
|
|
92
|
+
val surface = rendererSurface
|
|
93
|
+
rendererSurface = null
|
|
94
|
+
rendererSurfaceId = null
|
|
95
|
+
isRendererSurfaceStarted = false
|
|
96
|
+
|
|
97
|
+
if (surface == null) {
|
|
98
|
+
return@runOnMainSync
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
surface.stop()
|
|
102
|
+
surface.clear()
|
|
103
|
+
surface.detach()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
override fun onDropView() {
|
|
108
|
+
disposeRendererSurface()
|
|
109
|
+
super.onDropView()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun setDataSource(dataSource: HybridNativeListDataSourceSpec) {
|
|
113
|
+
val nativeDataSource = dataSource as? HybridNativeListDataSource
|
|
114
|
+
?: throw IllegalStateException("NativeListDataSource must be created by react-native-list.")
|
|
115
|
+
|
|
116
|
+
runOnMain {
|
|
117
|
+
val existingDataSource = this.dataSource
|
|
118
|
+
if (existingDataSource === nativeDataSource) {
|
|
119
|
+
return@runOnMain
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.dataSource?.observer = null
|
|
123
|
+
this.dataSource = nativeDataSource
|
|
124
|
+
nativeDataSource.observer = this
|
|
125
|
+
val nativeAdapter = ensureAdapter()
|
|
126
|
+
nativeAdapter.dataSource = nativeDataSource
|
|
127
|
+
nativeAdapter.retainMeasuredContent(nativeDataSource)
|
|
128
|
+
nativeAdapter.notifyDataSetChanged()
|
|
129
|
+
scheduleRecyclerViewLayout()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
override fun setLayout(layout: HybridNativeListLayoutSpec) {
|
|
134
|
+
val layoutProvider = layout as? NativeListLayoutProvider
|
|
135
|
+
?: throw IllegalStateException("NativeListLayout must provide a platform layout.")
|
|
136
|
+
|
|
137
|
+
runOnMain {
|
|
138
|
+
itemContentInsets = layoutProvider.itemContentInsets(reactContext)
|
|
139
|
+
val nativeAdapter = adapter
|
|
140
|
+
if (nativeAdapter != null) {
|
|
141
|
+
nativeAdapter.itemContentInsets = itemContentInsets
|
|
142
|
+
nativeAdapter.notifyDataSetChanged()
|
|
143
|
+
}
|
|
144
|
+
layoutProvider.applyTo(view, reactContext)
|
|
145
|
+
scheduleRecyclerViewLayout()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override fun dataSourceDidReload(diffResult: DiffUtil.DiffResult?, animated: Boolean) {
|
|
150
|
+
runOnMain {
|
|
151
|
+
val nativeAdapter = ensureAdapter()
|
|
152
|
+
val nativeDataSource = dataSource
|
|
153
|
+
if (nativeDataSource != null) {
|
|
154
|
+
nativeAdapter.retainMeasuredContent(nativeDataSource)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!animated || diffResult == null) {
|
|
158
|
+
nativeAdapter.notifyDataSetChanged()
|
|
159
|
+
scheduleRecyclerViewLayout()
|
|
160
|
+
return@runOnMain
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
diffResult.dispatchUpdatesTo(nativeAdapter)
|
|
164
|
+
scheduleRecyclerViewLayout()
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
override fun dataSourceDidInsert(index: Int) {
|
|
169
|
+
runOnMain {
|
|
170
|
+
val nativeAdapter = ensureAdapter()
|
|
171
|
+
nativeAdapter.notifyItemInserted(index)
|
|
172
|
+
scheduleRecyclerViewLayout()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
override fun dataSourceDidUpdate(index: Int, previousItem: NativeListItem) {
|
|
177
|
+
runOnMain {
|
|
178
|
+
val nativeAdapter = ensureAdapter()
|
|
179
|
+
nativeAdapter.notifyItemChanged(index)
|
|
180
|
+
scheduleRecyclerViewLayout()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
override fun dataSourceDidRemove(index: Int, removedItem: NativeListItem) {
|
|
185
|
+
runOnMain {
|
|
186
|
+
val nativeAdapter = ensureAdapter()
|
|
187
|
+
nativeAdapter.notifyItemRemoved(index)
|
|
188
|
+
scheduleRecyclerViewLayout()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
override fun dataSourceDidMove(fromIndex: Int, toIndex: Int) {
|
|
193
|
+
runOnMain {
|
|
194
|
+
val nativeAdapter = ensureAdapter()
|
|
195
|
+
nativeAdapter.notifyItemMoved(fromIndex, toIndex)
|
|
196
|
+
scheduleRecyclerViewLayout()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private fun ensureAdapter(): NativeListAdapter {
|
|
201
|
+
if (isDisposed) {
|
|
202
|
+
throw IllegalStateException("Cannot create adapter after list was disposed.")
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
val existingAdapter = adapter
|
|
206
|
+
if (existingAdapter != null) {
|
|
207
|
+
return existingAdapter
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
val nativeAdapter = NativeListAdapter(
|
|
211
|
+
reactContext = reactContext,
|
|
212
|
+
createView = { type ->
|
|
213
|
+
createNativeView(type)
|
|
214
|
+
},
|
|
215
|
+
updateView = { reactTag, item, index ->
|
|
216
|
+
val capturedCallback = updateViewCallback
|
|
217
|
+
?: throw IllegalStateException("UpdateView callback is not set.")
|
|
218
|
+
capturedCallback(reactTag, item, index)
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
nativeAdapter.itemContentInsets = itemContentInsets
|
|
222
|
+
nativeAdapter.dataSource = dataSource
|
|
223
|
+
adapter = nativeAdapter
|
|
224
|
+
attachAdapterIfRendererReady()
|
|
225
|
+
return nativeAdapter
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private fun attachAdapterIfRendererReady() {
|
|
229
|
+
val nativeAdapter = adapter
|
|
230
|
+
?: return
|
|
231
|
+
|
|
232
|
+
val surfaceId = rendererSurfaceId
|
|
233
|
+
if (surfaceId != null && !isRendererSurfaceStarted) {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
val currentAdapter = view.adapter
|
|
238
|
+
if (currentAdapter === nativeAdapter) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
view.adapter = nativeAdapter
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private fun ensureRendererSurface(): Int {
|
|
246
|
+
val existingSurfaceId = rendererSurfaceId
|
|
247
|
+
if (existingSurfaceId != null) {
|
|
248
|
+
return existingSurfaceId
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
val context: ReactApplicationContext = NitroModules.applicationContext
|
|
252
|
+
?: throw IllegalStateException("ReactApplicationContext is null! Is Nitro installed?")
|
|
253
|
+
val reactActivity = context.currentActivity as? ReactActivity
|
|
254
|
+
?: throw IllegalStateException("Current activity is not a ReactActivity!")
|
|
255
|
+
val reactHost = reactActivity.reactActivityDelegate.reactHost
|
|
256
|
+
?: throw IllegalStateException("ReactNativeHost is null!")
|
|
257
|
+
|
|
258
|
+
val surface = reactHost.createSurface(reactContext, "", null)
|
|
259
|
+
val surfaceView = surface.view as? ReactSurfaceView
|
|
260
|
+
?: throw IllegalStateException("Surface view is not a ReactSurfaceView!")
|
|
261
|
+
val surfaceId = surfaceView.getRootViewTag()
|
|
262
|
+
|
|
263
|
+
rendererSurface = surface
|
|
264
|
+
rendererSurfaceId = surfaceId
|
|
265
|
+
isRendererSurfaceStarted = false
|
|
266
|
+
isDisposed = false
|
|
267
|
+
|
|
268
|
+
val startTask = surface.start()
|
|
269
|
+
val waitThread = Thread {
|
|
270
|
+
try {
|
|
271
|
+
startTask.waitForCompletion()
|
|
272
|
+
view.post {
|
|
273
|
+
val startError = startTask.getError()
|
|
274
|
+
if (startError != null) {
|
|
275
|
+
throw startError
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (isDisposed) {
|
|
279
|
+
return@post
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
val currentSurfaceId = rendererSurfaceId
|
|
283
|
+
if (currentSurfaceId != surfaceId) {
|
|
284
|
+
return@post
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
isRendererSurfaceStarted = true
|
|
288
|
+
attachAdapterIfRendererReady()
|
|
289
|
+
scheduleRecyclerViewLayout()
|
|
290
|
+
}
|
|
291
|
+
} catch (throwable: Throwable) {
|
|
292
|
+
view.post {
|
|
293
|
+
throw throwable
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
waitThread.name = "UiListSurfaceStart-$surfaceId"
|
|
298
|
+
waitThread.start()
|
|
299
|
+
|
|
300
|
+
return surfaceId
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private fun createNativeView(type: String): View {
|
|
304
|
+
if (isDisposed) {
|
|
305
|
+
throw IllegalStateException("Cannot create view after list was disposed.")
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
val capturedCallback = createViewCallback
|
|
309
|
+
?: throw IllegalStateException("CreateView callback is not set.")
|
|
310
|
+
|
|
311
|
+
val viewTag = capturedCallback(type).toInt()
|
|
312
|
+
val fabricUiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
|
|
313
|
+
?: throw IllegalStateException("Fabric UIManager is null. Is Fabric enabled?")
|
|
314
|
+
|
|
315
|
+
val resolvedView = fabricUiManager.resolveView(viewTag)
|
|
316
|
+
?: throw IllegalStateException("Could not resolve view with tag $viewTag.")
|
|
317
|
+
|
|
318
|
+
val parent = resolvedView.parent as? ViewGroup
|
|
319
|
+
?: throw IllegalStateException("View with tag $viewTag has no parent.")
|
|
320
|
+
val childIndex = parent.indexOfChild(resolvedView)
|
|
321
|
+
parent.removeViewAt(childIndex)
|
|
322
|
+
|
|
323
|
+
if (resolvedView.parent != null) {
|
|
324
|
+
throw IllegalStateException("View with tag $viewTag still has a parent after removing.")
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
parent.addView(View(reactContext), childIndex)
|
|
328
|
+
|
|
329
|
+
return resolvedView
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
private fun scheduleRecyclerViewLayout() {
|
|
333
|
+
val surfaceId = rendererSurfaceId
|
|
334
|
+
if (surfaceId != null && !isRendererSurfaceStarted) {
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (isRecyclerViewLayoutScheduled) {
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
isRecyclerViewLayoutScheduled = true
|
|
343
|
+
view.post {
|
|
344
|
+
isRecyclerViewLayoutScheduled = false
|
|
345
|
+
performRecyclerViewLayoutIfReady()
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private fun performRecyclerViewLayoutIfReady() {
|
|
350
|
+
val viewWidth = view.width
|
|
351
|
+
val viewHeight = view.height
|
|
352
|
+
if (!view.isAttachedToWindow || viewWidth <= 0 || viewHeight <= 0) {
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// React Native has already assigned bounds, but RecyclerView requestLayout()
|
|
357
|
+
// is not always traversed from this Fabric-hosted view. Drive RecyclerView's
|
|
358
|
+
// pending adapter updates with the current RN layout.
|
|
359
|
+
val widthSpec = View.MeasureSpec.makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY)
|
|
360
|
+
val heightSpec = View.MeasureSpec.makeMeasureSpec(viewHeight, View.MeasureSpec.EXACTLY)
|
|
361
|
+
view.measure(widthSpec, heightSpec)
|
|
362
|
+
view.layout(view.left, view.top, view.right, view.bottom)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private fun runOnMain(block: () -> Unit) {
|
|
366
|
+
val isMainThread = Looper.myLooper() == Looper.getMainLooper()
|
|
367
|
+
if (isMainThread) {
|
|
368
|
+
block()
|
|
369
|
+
} else {
|
|
370
|
+
view.post(block)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private fun <T> runOnMainSync(block: () -> T): T {
|
|
375
|
+
val isMainThread = Looper.myLooper() == Looper.getMainLooper()
|
|
376
|
+
if (isMainThread) {
|
|
377
|
+
return block()
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
val result = AtomicReference<Result<T>>()
|
|
381
|
+
val latch = CountDownLatch(1)
|
|
382
|
+
view.post {
|
|
383
|
+
try {
|
|
384
|
+
val value = block()
|
|
385
|
+
result.set(Result.success(value))
|
|
386
|
+
} catch (throwable: Throwable) {
|
|
387
|
+
result.set(Result.failure(throwable))
|
|
388
|
+
} finally {
|
|
389
|
+
latch.countDown()
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
latch.await()
|
|
393
|
+
val capturedResult = result.get()
|
|
394
|
+
?: throw IllegalStateException("UI thread did not return a result.")
|
|
395
|
+
return capturedResult.getOrThrow()
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private class ClippedRecyclerView(context: ThemedReactContext) : RecyclerView(context) {
|
|
399
|
+
override fun dispatchDraw(canvas: Canvas) {
|
|
400
|
+
// RN/Fabric parents may allow child drawing outside their bounds.
|
|
401
|
+
// Keep clipToPadding=false for list insets, but clip rows to the list viewport.
|
|
402
|
+
// Without this item could overflow the list bounds.
|
|
403
|
+
val saveCount = canvas.save()
|
|
404
|
+
canvas.clipRect(0, 0, width, height)
|
|
405
|
+
super.dispatchDraw(canvas)
|
|
406
|
+
canvas.restoreToCount(saveCount)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativelist
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
5
|
+
|
|
6
|
+
class HybridViewHolder(context: ThemedReactContext) : HybridViewHolderSpec() {
|
|
7
|
+
override val view: View
|
|
8
|
+
get() = TODO("Hm, would this here be the user's view?")
|
|
9
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativelist
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import android.view.ViewGroup
|
|
5
|
+
import android.widget.FrameLayout
|
|
6
|
+
import androidx.recyclerview.widget.RecyclerView
|
|
7
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
+
import kotlin.math.roundToInt
|
|
9
|
+
|
|
10
|
+
internal class NativeListAdapter(
|
|
11
|
+
private val reactContext: ThemedReactContext,
|
|
12
|
+
private val createView: (type: String) -> View,
|
|
13
|
+
private val updateView: (reactTag: Double, item: NativeListItem, index: Double) -> Boolean
|
|
14
|
+
) : RecyclerView.Adapter<NativeListAdapter.ViewHolder>() {
|
|
15
|
+
|
|
16
|
+
var dataSource: HybridNativeListDataSource? = null
|
|
17
|
+
private val viewTypeByItemType = mutableMapOf<String, Int>()
|
|
18
|
+
private val itemTypeByViewType = mutableMapOf<Int, String>()
|
|
19
|
+
private val measuredContentSizeByItemKey = mutableMapOf<String, PixelSize>()
|
|
20
|
+
private var nextViewType = 1
|
|
21
|
+
var itemContentInsets = ItemContentInsets(horizontal = 0, vertical = 0)
|
|
22
|
+
|
|
23
|
+
class ViewHolder(val container: FrameLayout) : RecyclerView.ViewHolder(container) {
|
|
24
|
+
var hostedView: View? = null
|
|
25
|
+
var itemType: String? = null
|
|
26
|
+
var reactTag: Int? = null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private data class PixelSize(
|
|
30
|
+
val width: Int?,
|
|
31
|
+
val height: Int?
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
private data class ResolvedPixelSize(
|
|
35
|
+
val width: Int,
|
|
36
|
+
val height: Int
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
override fun getItemViewType(position: Int): Int {
|
|
40
|
+
val item = requireDataSource().getItemAt(position)
|
|
41
|
+
val existingViewType = viewTypeByItemType[item.type]
|
|
42
|
+
if (existingViewType != null) {
|
|
43
|
+
return existingViewType
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
val viewType = nextViewType
|
|
47
|
+
nextViewType += 1
|
|
48
|
+
viewTypeByItemType[item.type] = viewType
|
|
49
|
+
itemTypeByViewType[viewType] = item.type
|
|
50
|
+
return viewType
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
|
54
|
+
val container = FrameLayout(parent.context)
|
|
55
|
+
container.layoutParams = RecyclerView.LayoutParams(
|
|
56
|
+
RecyclerView.LayoutParams.WRAP_CONTENT,
|
|
57
|
+
RecyclerView.LayoutParams.WRAP_CONTENT
|
|
58
|
+
)
|
|
59
|
+
val itemType = itemTypeByViewType[viewType]
|
|
60
|
+
?: throw IllegalStateException("Missing item type for viewType $viewType.")
|
|
61
|
+
val child = createView(itemType)
|
|
62
|
+
val existingParent = child.parent as? ViewGroup
|
|
63
|
+
existingParent?.removeView(child)
|
|
64
|
+
|
|
65
|
+
container.addView(child)
|
|
66
|
+
val holder = ViewHolder(container)
|
|
67
|
+
holder.hostedView = child
|
|
68
|
+
holder.itemType = itemType
|
|
69
|
+
holder.reactTag = child.id
|
|
70
|
+
return holder
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
|
74
|
+
val item = requireDataSource().getItemAt(position)
|
|
75
|
+
val child = requireHostedView(holder, item)
|
|
76
|
+
prepareChildLayoutForMeasurement(child, item)
|
|
77
|
+
val reactTag = holder.reactTag
|
|
78
|
+
if (reactTag != null) {
|
|
79
|
+
val reactTagDouble = reactTag.toDouble()
|
|
80
|
+
val positionDouble = position.toDouble()
|
|
81
|
+
updateView(reactTagDouble, item, positionDouble)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
captureMeasuredContentSize(item.key, child)
|
|
85
|
+
val contentSize = resolvedContentSize(item)
|
|
86
|
+
bindContainerLayout(holder.container, contentSize)
|
|
87
|
+
bindChildLayout(child, contentSize)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
override fun getItemCount(): Int {
|
|
91
|
+
return dataSource?.getCountAsInt() ?: 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fun retainMeasuredContent(dataSource: HybridNativeListDataSource) {
|
|
95
|
+
val activeKeys = mutableSetOf<String>()
|
|
96
|
+
val itemCount = dataSource.getCountAsInt()
|
|
97
|
+
for (index in 0 until itemCount) {
|
|
98
|
+
val item = dataSource.getItemAt(index)
|
|
99
|
+
activeKeys.add(item.key)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
val measuredSizeIterator = measuredContentSizeByItemKey.keys.iterator()
|
|
103
|
+
while (measuredSizeIterator.hasNext()) {
|
|
104
|
+
val itemKey = measuredSizeIterator.next()
|
|
105
|
+
if (activeKeys.contains(itemKey)) {
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
measuredSizeIterator.remove()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private fun requireDataSource(): HybridNativeListDataSource {
|
|
113
|
+
return dataSource ?: throw IllegalStateException("NativeListDataSource is not set.")
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private fun requireHostedView(holder: ViewHolder, item: NativeListItem): View {
|
|
117
|
+
if (holder.itemType != item.type) {
|
|
118
|
+
throw IllegalStateException(
|
|
119
|
+
"RecyclerView supplied holder for type '${holder.itemType}' " +
|
|
120
|
+
"to item type '${item.type}'."
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
return holder.hostedView ?: throw IllegalStateException("ViewHolder has no hosted view.")
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private fun captureMeasuredContentSize(itemKey: String, view: View) {
|
|
127
|
+
val existingSize = measuredContentSizeByItemKey[itemKey]
|
|
128
|
+
val measuredWidth = positiveDimension(view.measuredWidth)
|
|
129
|
+
val measuredHeight = positiveDimension(view.measuredHeight)
|
|
130
|
+
val viewWidth = positiveDimension(view.width)
|
|
131
|
+
val viewHeight = positiveDimension(view.height)
|
|
132
|
+
val layoutWidth = positiveDimension(view.layoutParams?.width)
|
|
133
|
+
val layoutHeight = positiveDimension(view.layoutParams?.height)
|
|
134
|
+
val width = measuredWidth ?: viewWidth ?: layoutWidth
|
|
135
|
+
val height = measuredHeight ?: viewHeight ?: layoutHeight
|
|
136
|
+
|
|
137
|
+
val nextWidth = existingSize?.width ?: width
|
|
138
|
+
val nextHeight = existingSize?.height ?: height
|
|
139
|
+
if (nextWidth == null && nextHeight == null) {
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
measuredContentSizeByItemKey[itemKey] = PixelSize(
|
|
144
|
+
width = nextWidth,
|
|
145
|
+
height = nextHeight
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private fun resolvedContentSize(item: NativeListItem): ResolvedPixelSize {
|
|
150
|
+
val measuredSize = measuredContentSizeByItemKey[item.key]
|
|
151
|
+
val width = item.width?.let { toPixels(it) } ?: measuredSize?.width
|
|
152
|
+
val height = item.height?.let { toPixels(it) } ?: measuredSize?.height
|
|
153
|
+
|
|
154
|
+
if (width == null) {
|
|
155
|
+
throw IllegalStateException(
|
|
156
|
+
"Missing width for item type '${item.type}'. " +
|
|
157
|
+
"Provide width from getItemSize or render a measurable shell."
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
if (height == null) {
|
|
161
|
+
throw IllegalStateException(
|
|
162
|
+
"Missing height for item type '${item.type}'. " +
|
|
163
|
+
"Provide height from getItemSize or render a measurable shell."
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return ResolvedPixelSize(width, height)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private fun bindContainerLayout(container: FrameLayout, contentSize: ResolvedPixelSize) {
|
|
171
|
+
val horizontalInset = itemContentInsets.horizontal
|
|
172
|
+
val verticalInset = itemContentInsets.vertical
|
|
173
|
+
val containerWidth = contentSize.width + horizontalInset * 2
|
|
174
|
+
val containerHeight = contentSize.height + verticalInset * 2
|
|
175
|
+
val layoutParams = container.layoutParams as? RecyclerView.LayoutParams
|
|
176
|
+
?: RecyclerView.LayoutParams(containerWidth, containerHeight)
|
|
177
|
+
layoutParams.width = containerWidth
|
|
178
|
+
layoutParams.height = containerHeight
|
|
179
|
+
container.layoutParams = layoutParams
|
|
180
|
+
container.setPadding(
|
|
181
|
+
horizontalInset,
|
|
182
|
+
verticalInset,
|
|
183
|
+
horizontalInset,
|
|
184
|
+
verticalInset
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private fun bindChildLayout(child: View, contentSize: ResolvedPixelSize) {
|
|
189
|
+
val layoutParams = FrameLayout.LayoutParams(contentSize.width, contentSize.height)
|
|
190
|
+
child.layoutParams = layoutParams
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private fun prepareChildLayoutForMeasurement(child: View, item: NativeListItem) {
|
|
194
|
+
if (item.width != null && item.height != null) {
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
val width = item.width?.let { toPixels(it) } ?: ViewGroup.LayoutParams.WRAP_CONTENT
|
|
199
|
+
val height = item.height?.let { toPixels(it) } ?: ViewGroup.LayoutParams.WRAP_CONTENT
|
|
200
|
+
val layoutParams = FrameLayout.LayoutParams(width, height)
|
|
201
|
+
child.layoutParams = layoutParams
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private fun positiveDimension(value: Int?): Int? {
|
|
205
|
+
if (value == null || value <= 0) {
|
|
206
|
+
return null
|
|
207
|
+
}
|
|
208
|
+
return value
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private fun toPixels(value: Double): Int {
|
|
212
|
+
val density = reactContext.resources.displayMetrics.density
|
|
213
|
+
val pixels = value * density
|
|
214
|
+
val rounded = pixels.roundToInt()
|
|
215
|
+
return rounded.coerceAtLeast(1)
|
|
216
|
+
}
|
|
217
|
+
}
|