Usando Jetpack Compose em Adições de View com WindowManager

No desenvolvimento de aplicações Android, a adição de views à tela via WindowManager.addView() é uma abordagem comum para interfaces sobrepostas. Integrar o Jetpack Compose nesse cenário requer atenção especial ao ciclo de vida, pois o ComposeView depende de proprietários de ciclo de vida e estado para funcionar corretamente.

Uma tentativa direta de usar ComposeView com o WindowManager pode resultar em erros. Por exemplo, ao adicionar uma ComposeView simplesmente:

val layoutParams = WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
)

val composableElement = ComposeView(context).apply {
    setContent {
        Text(text = "Exemplo de Compose")
    }
}

windowManager.addView(composableElement, layoutParams)

Isso pode causar uma exceção em tempo de execução indicando que o ViewTreeLifecycleOwner não foi encontrado. A raiz do problema é que o ComposeView precisa de um LifecycleOwner para gerenciar recomposições e estados.

Para resolver isso, é necessário criar um proprietário de ciclo de vida personalizado. A implementação envolve definir uma classe que implemente as interfaces necessárias:

class OverlayLifecycleProvider : LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
    private val lifecycleRegistry = LifecycleRegistry(this)
    private val savedStateController = SavedStateRegistryController.create(this)
    private val modelStore = ViewModelStore()

    override val lifecycle: Lifecycle get() = lifecycleRegistry
    override val savedStateRegistry: SavedStateRegistry get() = savedStateController.savedStateRegistry
    override val viewModelStore: ViewModelStore get() = modelStore

    fun initialize() {
        savedStateController.performRestore(null)
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    fun activate() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
    }

    fun deactivate() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
    }

    fun terminate() {
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        modelStore.clear()
    }

    fun attachToView(view: View) {
        ViewTreeLifecycleOwner.set(view, this)
        ViewTreeViewModelStoreOwner.set(view, this)
        ViewTreeSavedStateRegistryOwner.set(view, this)
    }
}

Após configurar o proprietário, associe-o à view antes de adicioná-la ao WindowManager. Além disso, é crucial configurar o recompositor para garantir que as atualizações de composição ocorrram:

val owner = OverlayLifecycleProvider().apply {
    initialize()
    attachToView(composableElement)
}

val recompositorContext = AndroidUiDispatcher.CurrentThread
val recompositor = Recomposer(recompositorContext)
composableElement.compositionContext = recompositor
CoroutineScope(recompositorContext).launch {
    recompositor.runRecomposeAndApplyChanges()
}

windowManager.addView(composableElement, layoutParams)

Lembre-se de gerenciar o ciclo de vida ao remover a view. Por exemplo, ao desativar:

windowManager.removeView(composableElement)
owner.deactivate()
owner.terminate()

Essa abordagem garante que o Jetpack Compose funcione corretamente em views gerenciadas pelo WindowManager, permitindo a utilização de componentes composáveis em sobreposições.

Tags: Jetpack Compose WindowManager android Lifecycle ViewModelStore

Publicado em 6-9 17:04 por Thomas