golia-expo-utils 1.0.5 → 1.0.7
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/expo/modules/goliaexpoutils/segmentedControl/ReactSegmentedControl.kt
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package expo.modules.goliaexpoutils.segmentedControl
|
|
2
2
|
|
|
3
|
+
// 引入扩展
|
|
3
4
|
import android.annotation.SuppressLint
|
|
4
5
|
import android.content.Context
|
|
5
6
|
import android.view.ViewGroup
|
|
@@ -33,10 +34,7 @@ class ReactSegmentedControl(context: Context, appContext: AppContext) :
|
|
|
33
34
|
private var isContentSet = false
|
|
34
35
|
|
|
35
36
|
private val composeView = ComposeView(context).apply {
|
|
36
|
-
layoutParams = ViewGroup.LayoutParams(
|
|
37
|
-
LayoutParams.MATCH_PARENT,
|
|
38
|
-
LayoutParams.MATCH_PARENT
|
|
39
|
-
)
|
|
37
|
+
layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
40
38
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
41
39
|
}
|
|
42
40
|
|
|
@@ -56,7 +54,7 @@ class ReactSegmentedControl(context: Context, appContext: AppContext) :
|
|
|
56
54
|
addView(composeView)
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
// 3. 设置内容
|
|
57
|
+
// 3. 设置内容 (或复活)
|
|
60
58
|
if (!isContentSet) {
|
|
61
59
|
composeView.setContent {
|
|
62
60
|
SegmentedControl(
|
|
@@ -85,17 +83,31 @@ class ReactSegmentedControl(context: Context, appContext: AppContext) :
|
|
|
85
83
|
isContentSet = true
|
|
86
84
|
}
|
|
87
85
|
|
|
86
|
+
composeView.visibility = VISIBLE
|
|
88
87
|
requestLayout()
|
|
88
|
+
post {
|
|
89
|
+
measure(
|
|
90
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
91
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
92
|
+
)
|
|
93
|
+
layout(left, top, right, bottom)
|
|
94
|
+
invalidate()
|
|
95
|
+
}
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
override fun onDetachedFromWindow() {
|
|
99
|
+
// 4. 必须移除,防止后台 Crash
|
|
92
100
|
if (composeView.parent == this) {
|
|
93
101
|
removeView(composeView)
|
|
94
102
|
}
|
|
103
|
+
|
|
104
|
+
// 5. 标记复活
|
|
105
|
+
isContentSet = false
|
|
106
|
+
|
|
95
107
|
super.onDetachedFromWindow()
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
//
|
|
110
|
+
// 6. 手动布局修正
|
|
99
111
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
100
112
|
super.onLayout(changed, left, top, right, bottom)
|
|
101
113
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
package expo.modules.goliaexpoutils.waterView
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// 引入扩展函数
|
|
4
4
|
import android.annotation.SuppressLint
|
|
5
5
|
import android.content.Context
|
|
6
6
|
import android.view.View
|
|
@@ -55,6 +55,7 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
55
55
|
private val onPressOut by EventDispatcher()
|
|
56
56
|
private val commandChannel = Channel<WaterCommand>(Channel.BUFFERED)
|
|
57
57
|
|
|
58
|
+
// 标记 Compose 内容是否已设置
|
|
58
59
|
private var isContentSet = false
|
|
59
60
|
|
|
60
61
|
private val childContainer = FrameLayout(context).apply {
|
|
@@ -63,6 +64,7 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
63
64
|
|
|
64
65
|
private val composeView = ComposeView(context).apply {
|
|
65
66
|
layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
67
|
+
// 策略:Detach 时自动销毁 Composition 释放资源
|
|
66
68
|
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -74,15 +76,15 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
74
76
|
override fun onAttachedToWindow() {
|
|
75
77
|
super.onAttachedToWindow()
|
|
76
78
|
|
|
77
|
-
// 1. 注入生命周期
|
|
79
|
+
// 1. 注入生命周期 (防崩关键)
|
|
78
80
|
composeView.enforceLifecycle(context)
|
|
79
81
|
|
|
80
|
-
// 2. 动态挂载 View
|
|
82
|
+
// 2. 动态挂载 View (此时 Activity 肯定存在)
|
|
81
83
|
if (composeView.parent == null) {
|
|
82
84
|
addView(composeView)
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
// 3. 设置内容
|
|
87
|
+
// 3. 设置内容 (或复活)
|
|
86
88
|
if (!isContentSet) {
|
|
87
89
|
composeView.setContent {
|
|
88
90
|
WaterViewEntry(
|
|
@@ -90,6 +92,7 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
90
92
|
props = props,
|
|
91
93
|
commandFlow = commandChannel.receiveAsFlow(),
|
|
92
94
|
onMoveDispatch = { x, y ->
|
|
95
|
+
// 空安全保护,防止 Bridgeless 模式下 NPE
|
|
93
96
|
if (appContext.reactContext != null) {
|
|
94
97
|
try {
|
|
95
98
|
onMove(mapOf("x" to x, "y" to y))
|
|
@@ -104,38 +107,50 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
104
107
|
isContentSet = true
|
|
105
108
|
}
|
|
106
109
|
|
|
107
|
-
// 4.
|
|
110
|
+
// 4. 请求布局刷新
|
|
111
|
+
composeView.visibility = VISIBLE
|
|
108
112
|
requestLayout()
|
|
113
|
+
post {
|
|
114
|
+
measure(
|
|
115
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
116
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
117
|
+
)
|
|
118
|
+
layout(left, top, right, bottom)
|
|
119
|
+
invalidate()
|
|
120
|
+
}
|
|
109
121
|
}
|
|
110
122
|
|
|
111
123
|
override fun onDetachedFromWindow() {
|
|
112
|
-
//
|
|
124
|
+
// 5. 必须移除!防止 RNSAC 等库导致 View 处于 detached 状态时被 Fabric 测量从而引发 Crash
|
|
113
125
|
if (composeView.parent == this) {
|
|
114
126
|
removeView(composeView)
|
|
115
127
|
}
|
|
128
|
+
|
|
129
|
+
// 6. 标记复活:重置状态,允许下次 Attach 时重新 setContent (因为 DisposeOnDetached 已经销毁了引擎)
|
|
130
|
+
isContentSet = false
|
|
131
|
+
|
|
116
132
|
super.onDetachedFromWindow()
|
|
117
133
|
}
|
|
118
134
|
|
|
119
|
-
// ✅ 终极修正:手动接管布局
|
|
120
|
-
// 解决“View加上去了但是宽高为0
|
|
135
|
+
// ✅ 7. 终极修正:手动接管布局
|
|
136
|
+
// 解决“View加上去了但是宽高为0不显示”的问题,无视 Fabric 的测量延迟
|
|
121
137
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
122
138
|
super.onLayout(changed, left, top, right, bottom)
|
|
123
139
|
|
|
124
|
-
// 如果 composeView 已经挂载,强行设置它的尺寸等于父容器
|
|
125
140
|
if (composeView.parent == this) {
|
|
126
141
|
val width = right - left
|
|
127
142
|
val height = bottom - top
|
|
128
143
|
|
|
129
|
-
//
|
|
144
|
+
// 强行设置 ComposeView 的大小等于父容器
|
|
130
145
|
composeView.measure(
|
|
131
146
|
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
132
147
|
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
133
148
|
)
|
|
134
|
-
// 2. 手动布局 (放在 (0,0) 位置)
|
|
135
149
|
composeView.layout(0, 0, width, height)
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
|
|
153
|
+
// View 管理:确保 composeView 在底部,RN 子 View 在 childContainer 中
|
|
139
154
|
override fun addView(child: View?, index: Int) {
|
|
140
155
|
if (child == composeView) {
|
|
141
156
|
super.addView(child, index)
|
|
@@ -157,7 +172,7 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
157
172
|
if (childContainer.isNotEmpty()) {
|
|
158
173
|
try {
|
|
159
174
|
childContainer.removeViewAt(0)
|
|
160
|
-
} catch (_:
|
|
175
|
+
} catch (_: Exception) {
|
|
161
176
|
}
|
|
162
177
|
}
|
|
163
178
|
}
|
|
@@ -167,6 +182,7 @@ class ReactWaterView(context: Context, appContext: AppContext) : ExpoView(contex
|
|
|
167
182
|
}
|
|
168
183
|
}
|
|
169
184
|
|
|
185
|
+
// ... 下面的 Composable 代码保持不变 ...
|
|
170
186
|
@Composable
|
|
171
187
|
private fun WaterViewEntry(
|
|
172
188
|
childContainer: FrameLayout,
|