From f9ba7e5766e811cfce78cf1208ae1f8a6086d1d7 Mon Sep 17 00:00:00 2001 From: Bruce Mok Date: Fri, 27 Mar 2026 16:10:12 +1100 Subject: [PATCH] perf: improve MarkerComposable rendering performance --- .../com/google/maps/android/compose/Marker.kt | 86 ++++++++++--------- .../RememberComposeBitmapDescriptor.kt | 51 ++++++----- 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt index f2333f8c..2b6d84ba 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt @@ -341,27 +341,28 @@ public fun MarkerComposable( content: @UiComposable @Composable () -> Unit, ) { val icon = rememberComposeBitmapDescriptor(*keys) { content() } - - MarkerImpl( - state = state, - contentDescription = contentDescription, - alpha = alpha, - anchor = anchor, - draggable = draggable, - flat = flat, - icon = icon, - infoWindowAnchor = infoWindowAnchor, - rotation = rotation, - snippet = snippet, - tag = tag, - title = title, - visible = visible, - zIndex = zIndex, - onClick = onClick, - onInfoWindowClick = onInfoWindowClick, - onInfoWindowClose = onInfoWindowClose, - onInfoWindowLongClick = onInfoWindowLongClick, - ) + if (icon != null) { + MarkerImpl( + state = state, + contentDescription = contentDescription, + alpha = alpha, + anchor = anchor, + draggable = draggable, + flat = flat, + icon = icon, + infoWindowAnchor = infoWindowAnchor, + rotation = rotation, + snippet = snippet, + tag = tag, + title = title, + visible = visible, + zIndex = zIndex, + onClick = onClick, + onInfoWindowClick = onInfoWindowClick, + onInfoWindowClose = onInfoWindowClose, + onInfoWindowLongClick = onInfoWindowLongClick, + ) + } } /** @@ -489,27 +490,28 @@ public fun MarkerInfoWindowComposable( content: @UiComposable @Composable () -> Unit, ) { val icon = rememberComposeBitmapDescriptor(*keys) { content() } - - MarkerImpl( - state = state, - alpha = alpha, - anchor = anchor, - draggable = draggable, - flat = flat, - icon = icon, - infoWindowAnchor = infoWindowAnchor, - rotation = rotation, - snippet = snippet, - tag = tag, - title = title, - visible = visible, - zIndex = zIndex, - onClick = onClick, - onInfoWindowClick = onInfoWindowClick, - onInfoWindowClose = onInfoWindowClose, - onInfoWindowLongClick = onInfoWindowLongClick, - infoWindow = infoContent, - ) + if (icon != null) { + MarkerImpl( + state = state, + alpha = alpha, + anchor = anchor, + draggable = draggable, + flat = flat, + icon = icon, + infoWindowAnchor = infoWindowAnchor, + rotation = rotation, + snippet = snippet, + tag = tag, + title = title, + visible = visible, + zIndex = zIndex, + onClick = onClick, + onInfoWindowClick = onInfoWindowClick, + onInfoWindowClose = onInfoWindowClose, + onInfoWindowLongClick = onInfoWindowLongClick, + infoWindow = infoContent, + ) + } } /** diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/RememberComposeBitmapDescriptor.kt b/maps-compose/src/main/java/com/google/maps/android/compose/RememberComposeBitmapDescriptor.kt index 98332b76..3c1a943f 100644 --- a/maps-compose/src/main/java/com/google/maps/android/compose/RememberComposeBitmapDescriptor.kt +++ b/maps-compose/src/main/java/com/google/maps/android/compose/RememberComposeBitmapDescriptor.kt @@ -19,52 +19,65 @@ package com.google.maps.android.compose import android.view.View import android.view.ViewGroup import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionContext +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCompositionContext import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalView import androidx.core.graphics.applyCanvas +import androidx.core.graphics.createBitmap import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory -import androidx.core.graphics.createBitmap +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @MapsComposeExperimentalApi @Composable public fun rememberComposeBitmapDescriptor( vararg keys: Any, content: @Composable () -> Unit, -): BitmapDescriptor { +): BitmapDescriptor? { + var bitmapDescriptor by remember { mutableStateOf(null) } val parent = LocalView.current as ViewGroup val compositionContext = rememberCompositionContext() val currentContent by rememberUpdatedState(content) + val composeView = remember { ComposeView(parent.context) } - return remember(parent, compositionContext, currentContent, *keys) { - renderComposableToBitmapDescriptor(parent, compositionContext, currentContent) - } -} - -private val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) - -private fun renderComposableToBitmapDescriptor( - parent: ViewGroup, - compositionContext: CompositionContext, - content: @Composable () -> Unit, -): BitmapDescriptor { - val composeView = - ComposeView(parent.context) + DisposableEffect(Unit) { + composeView .apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, ) setParentCompositionContext(compositionContext) - setContent(content) + setContent(currentContent) } .also(parent::addView) + onDispose { + parent.removeView(composeView) + } + } + + LaunchedEffect(*keys) { + bitmapDescriptor = withContext(Dispatchers.IO) { + renderComposableToBitmapDescriptor(composeView) + } + } + + return bitmapDescriptor +} +private val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + +private fun renderComposableToBitmapDescriptor( + composeView: ComposeView, +): BitmapDescriptor { composeView.measure(measureSpec, measureSpec) if (composeView.measuredWidth == 0 || composeView.measuredHeight == 0) { @@ -79,7 +92,5 @@ private fun renderComposableToBitmapDescriptor( bitmap.applyCanvas { composeView.draw(this) } - parent.removeView(composeView) - return BitmapDescriptorFactory.fromBitmap(bitmap) }