AndroidView
Component in Compose Ui
Composes an Android [View] obtained from [factory]. The [factory] block will be called exactly once to obtain the [View] being composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating the [View], the [factory] block can also be used to perform one-off initializations and [View] constant properties' setting. The [update] block can run multiple times (on the UI thread as well) due to recomposition, and it is the right place to set the new properties. Note that the block will also run once right after the [factory] block completes.
[AndroidView] is commonly needed for using Views that are infeasible to be reimplemented in Compose and there is no corresponding Compose API. Common examples for the moment are WebView, SurfaceView, AdView, etc.
This overload of [AndroidView] does not automatically pool or reuse Views. If placed inside of a reusable container (including inside a [LazyRow][androidx.compose.foundation.lazy.LazyRow] or [LazyColumn][androidx.compose.foundation.lazy.LazyColumn]), the View instances will always be discarded and recreated if the composition hierarchy containing the AndroidView changes, even if its group structure did not change and the View could have conceivably been reused.
To opt-in for View reuse, call the overload of [AndroidView] that accepts an onReset
callback,
and provide a non-null implementation for this callback. Since it is expensive to discard and
recreate View instances, reusing Views can lead to noticeable performance improvements —
especially when building a scrolling list of [AndroidViews][AndroidView]. It is highly
recommended to opt-in to View reuse when possible.
[AndroidView] will not clip its content to the layout bounds. Use [View.setClipToOutline] on the child View to clip the contents, if desired. Developers will likely want to do this with all subclasses of SurfaceView to keep its contents contained.
Last updated:
Installation
dependencies {
implementation("androidx.compose.ui:ui:1.8.0-alpha04")
}
Overloads
@Composable
@UiComposable
fun <T : View> AndroidView(
factory: (Context) -> T,
modifier: Modifier = Modifier,
update: (T) -> Unit = NoOpUpdate
)
Parameters
name | description |
---|---|
factory | The block creating the [View] to be composed. |
modifier | The modifier to be applied to the layout. |
update | A callback to be invoked after the layout is inflated and upon recomposition to update the information and state of the view. |
@Composable
@UiComposable
fun <T : View> AndroidView(
factory: (Context) -> T,
modifier: Modifier = Modifier,
onReset: ((T) -> Unit)? = null,
onRelease: (T) -> Unit = NoOpUpdate,
update: (T) -> Unit = NoOpUpdate
)
Parameters
name | description |
---|---|
factory | The block creating the [View] to be composed. |
modifier | The modifier to be applied to the layout. |
onReset | A callback invoked as a signal that the view is about to be attached to the composition hierarchy in a different context than its original creation. This callback is invoked before [update] and should prepare the view for general reuse. If null or not specified, the AndroidView instance will not support reuse, and the View instance will always be discarded whenever the AndroidView is moved or removed from the composition hierarchy. |
onRelease | A callback invoked as a signal that this view instance has exited the composition hierarchy entirely and will not be reused again. Any additional resources used by the View should be freed at this time. |
update | A callback to be invoked after the layout is inflated and upon recomposition to update the information and state of the view. |
Code Examples
ViewInComposeNestedScrollInteropSample
@Composable
fun ViewInComposeNestedScrollInteropSample() {
Box(
Modifier.fillMaxSize()
.scrollable(
rememberScrollableState {
// view world deltas should be reflected in compose world
// components that participate in nested scrolling
it
},
Orientation.Vertical
)
) {
AndroidView({ context ->
LayoutInflater.from(context).inflate(android.R.layout.activity_list_item, null).apply {
// Nested Scroll Interop will be Enabled when
// nested scroll is enabled for the root view
ViewCompat.setNestedScrollingEnabled(this, true)
}
})
}
}
AndroidViewSample
@Suppress("SetTextI18n")
@Composable
fun AndroidViewSample() {
// Compose a TextView.
AndroidView({ context -> TextView(context).apply { text = "This is a TextView" } })
// Compose a View and update its size based on state. Note the modifiers.
var size by remember { mutableStateOf(20) }
AndroidView(::View, Modifier.clickable { size += 20 }.background(Color.Blue)) { view ->
view.layoutParams = ViewGroup.LayoutParams(size, size)
}
}
ReusableAndroidViewInLazyColumnSample
@Composable
fun ReusableAndroidViewInLazyColumnSample() {
val urls =
listOf(
"https://developer.android.com/jetpack/compose",
"https://google.github.io/accompanist/",
"https://android-developers.googleblog.com/",
"https://io.google/",
// ...
)
LazyVerticalGrid(columns = GridCells.Adaptive(512.dp)) {
items(urls) { url ->
AndroidView(
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
webViewClient =
object : WebViewClient() {
// Optional overrides for WebViewClient
}
}
},
modifier = Modifier.fillMaxWidth().aspectRatio(1f),
update = { webView -> webView.loadUrl(url) },
onReset = { webView ->
webView.stopLoading()
webView.loadUrl("about:blank")
webView.clearHistory()
}
)
}
}
}
AndroidViewWithReleaseSample
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
@Composable
fun AndroidViewWithReleaseSample() {
// Compose a View that needs to be cleaned up when removed from the UI
class LifecycleAwareView(context: Context) : View(context) {
var lifecycle: Lifecycle? = null
set(value) {
field?.removeObserver(observer)
value?.addObserver(observer)
field = value
}
private val observer = LifecycleEventObserver { source, event ->
// React to the event
}
}
val lifecycle = LocalLifecycleOwner.current.lifecycle
AndroidView(
factory = { context -> LifecycleAwareView(context) },
update = { view -> view.lifecycle = lifecycle },
onRelease = { view ->
// Need to release the lifecycle to prevent a memory leak
view.lifecycle = null
}
)
}