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.
@@ -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
- // 必须移除,防止 Fabric 对离屏 View 进行测量导致崩溃
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
- // 1. 手动测量
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 (_: Throwable) {
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "golia-expo-utils",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "expo utils provided by golia.jp",
5
5
  "main": "build/index.js",
6
6
  "homepage": "https://expo.golia.jp/golia-expo-utils",