ComposeView杂记(持续更新)
导航
- onGloballyPositioned
- 关于AndroidView
onGloballyPositioned
onGloballyPositioned 是 Jetpack Compose 中一个非常常用、但也经常被误解的布局回调函数。它属于 Modifier 系统,用于在 布局完成后 获取组件的 尺寸 和 位置信息。
生命周期与触发时机:
回调在 布局阶段结束后 触发(即组件的大小与位置已经确定)。
当布局或父布局发生变化时,会再次触发。
注意:它不会在 每次 recomposition(重组)都触发,除非布局确实变化。
方法定义:
fun Modifier.onGloballyPositioned(onPositioned: (LayoutCoordinates) -> Unit
): Modifier
它接收一个回调参数 (LayoutCoordinates) -> Unit,当该 Composable 的 布局坐标信息确定(或变化)时,就会调用。
onGloballyPositioned 常用于以下场景:
| 用途 | 示例 |
|---|---|
| 获取组件的宽高 | 动态计算布局大小 |
| 获取组件在屏幕中的位置 | 弹窗、动画、连线等需要绝对坐标 |
| 跟踪布局变化 | 当某个组件移动或大小变化时响应 |
| 实现自定义布局逻辑 | 例如拖拽、对齐、锚点定位 |
LayoutCoordinates 是布局信息的载体,常用属性包括:
| 属性/方法 | 类型 | 含义 |
|---|---|---|
size | IntSize | 组件宽高(像素) |
positionInParent() | Offset | 相对父布局的位置,以 父布局 的左上角为原点 |
positionInRoot() | Offset | 相对根布局的位置 ,以 根布局(Root) 的左上角为原点 |
positionInWindow() | Offset | 相对系统窗口的位置(绝对屏幕坐标,更接近物理屏幕坐标) |
localToRoot(local: Offset) | Offset | 将局部坐标转换到根布局坐标 |
localToWindow(local: Offset) | Offset | 将局部坐标转换到屏幕坐标 |
parentCoordinates | LayoutCoordinates? | 父布局的坐标信息 |
isAttached | Boolean | 是否仍在界面树中 |
🌰 示例 1:获取组件宽高
var size by remember { mutableStateOf(IntSize.Zero) }Box(modifier = Modifier.size(150.dp).onGloballyPositioned { coordinates ->size = coordinates.size}
)Text("Width: ${size.width}, Height: ${size.height}")
🔹 coordinates.size 返回的是像素值(px),不是 dp。
如需转换:
with(LocalDensity.current) { coordinates.size.width.toDp() }
🌰 示例 2:获取组件位置
Box(modifier = Modifier.fillMaxSize()) {Text("Hello",modifier = Modifier.onGloballyPositioned { coordinates ->val posInRoot = coordinates.positionInRoot()val posInWindow = coordinates.positionInWindow()Log.d("ComposePos", "Root: $posInRoot, Window: $posInWindow")})
}
关于AndroidView
AndroidView 是 Jetpack Compose 提供的桥梁,用来在 Compose 中嵌入传统的 View(如 TextView、MapView、WebView、RecyclerView 等)。
基本用法:
AndroidView(factory = { context ->// 创建 ViewTextView(context).apply {text = "Hello from Android View"}},update = { view ->// 每次 recomposition 时更新view.text = "Updated text"},onRelease = { view ->// 释放资源}
)
| 参数 | 作用 |
|---|---|
factory: (Context) -> T | 创建并返回一个传统的 Android View |
update: (T) -> Unit | 当 Compose 重组(recompose)时更新该 View |
modifier: Modifier | Compose 的修饰符系统 |
onReset: (T) -> Unit | 当 update 之前或状态重置时调用(很少用) |
onRelease: (T) -> Unit ✅ | 当 View 从 Compose 树中被移除时调用(用于资源清理), 从 Compose 1.7.0 开始正式引入,只在 View 销毁时调用,不会在每次重组触发。 |
| 如果只是更新 UI,请使用 update |
当 Compose 移除该 AndroidView, 例如:
- 该 Composable 不再出现在界面上;
- 状态变化导致它被销毁;
- 宿主 Activity/Fragment 销毁;
Compose 会调用 onRelease,此时可以安全地做资源释放。
🌰 示例 1:WebView 的释放
AndroidView(factory = { context ->WebView(context).apply {loadUrl("https://example.com")}},update = { webView ->// 更新逻辑},onRelease = { webView ->webView.destroy() // ✅ 释放资源}
)
以前我们必须用 DisposableEffect,现在可以直接用 onRelease。
🌰 示例 2:DisposableEffect 做相同事情
早期我们必须这样写:
val context = LocalContext.current
val webView = remember { WebView(context) }DisposableEffect(Unit) {onDispose {webView.destroy()}
}AndroidView(factory = { webView },update = { it.loadUrl("https://example.com") }
)
现在,可以直接写成:
AndroidView(factory = { context -> WebView(context).apply { loadUrl("https://example.com") } },onRelease = { it.destroy() }
)
与 DisposableEffect 的区别:
| 特性 | onRelease(AndroidView 内置) | DisposableEffect(Compose 通用) |
|---|---|---|
| 所在层 | AndroidView 内部 | Compose 层通用 API |
| 生命周期绑定 | AndroidView 的 View 生命周期 | 任意键(key)或 Composition 生命周期 |
| 调用时机 | 当该 View 从 Composition 树中被移除时 | 当 key 改变或 Composition 离开时 |
| 常用于 | 传统 View 的释放 | 任意需要清理的资源(监听器、协程、传感器等) |
| 推荐场景 | WebView、MapView、CameraView 等释放 | Compose 状态、订阅、外部资源清理等 |
