Would it be possible to add a sample for the new ViewNode in Compose (in v2.1.0)?
Previously, I would use the ViewRenderable to render an AndroidView. However, as ViewNode has changed significantly in v2.1.0, I am unable to get my AndroidView to show properly.
This is my old code:
// Dummy AndroidView
val androidView =
android.view.View(context)
.apply {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
layout(0, 0, 10, 5)
setBackgroundColor(Color.WHITE)
}
ViewRenderable.builder()
.setView(context, androidView)
.build(engine)
.thenAccept {
val viewAttachmentManager = ViewAttachmentManager(context, sceneView)
viewAttachmentManager.onResume()
val childNode = ViewNode(
engine = engine,
modelLoader = modelLoader,
viewAttachmentManager = viewAttachmentManager,
).apply {
setRenderable(it)
}
childNodes.add(childNode)
Scene(
modifier = Modifier.fillMaxSize(),
engine = engine,
modelLoader = modelLoader,
cameraNode = cameraNode,
childNodes = childNodes,
view = view,
// ...
)
This is my broken attempt at trying to render the a child node:
val viewNodeWindowManager = rememberViewNodeManager()
val materialLoader = rememberMaterialLoader(engine)
// ...
rememberNode {
ViewNode(
engine = engine,
windowManager = viewNodeWindowManager,
materialLoader = materialLoader,
content = {
Text(text = context.getString("Some text"))
}
)
.also {
childNodes.add(it)
}
}
Scene(
modifier = Modifier.fillMaxSize(),
engine = engine,
modelLoader = modelLoader,
cameraNode = cameraNode,
childNodes = childNodes,
view = view,
// ...
viewNodeWindowManager = viewNodeWindowManager // added
)
Running the above results in the following crash:
FATAL EXCEPTION: main
Process: com.wilfredbtan.choreographic, PID: 31606
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.FrameLayout{68df235 V.E...... ......I. 0,0-0,0}
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:352)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:325)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.LifecycleAware$lambda$0(WindowRecomposer.android.kt:168)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.$r8$lambda$FWAPLXs0qWMqekhMr83xkKattCY(Unknown Source:0)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$$ExternalSyntheticLambda0.createRecomposer(D8$$SyntheticClass:0)
at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:224)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:300)
at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:244)
at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:251)
at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:283)
at android.view.View.dispatchAttachedToWindow(View.java:22479)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3686)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3695)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3695)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3669)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3116)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10885)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1301)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1309)
at android.view.Choreographer.doCallbacks(Choreographer.java:923)
at android.view.Choreographer.doFrame(Choreographer.java:852)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1283)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8757)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Could anyone guide me in the right direction to render a ViewNode properly in v2.1.0?
The problem is in ViewAttachmentManager. It adds FrameLayout to WindowManager and non of them have reference on LifecycleOwner.
Here is workaround I created temporary. Replace lib's ViewAttachmentManager on this one:
`import android.content.Context import android.graphics.PixelFormat import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.widget.FrameLayout import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeViewModelStoreOwner import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.google.ar.sceneform.rendering.ViewAttachmentManager
/**
-
Updated implementation of ViewAttachmentManager with access to frameLayout
-
Don't forget to add to lifecycle observers otherwise View will not be rendered */ class ComposableViewAttachmentManager( context: Context, private val lifecycleOwner: LifecycleOwner, private val viewModelStoreOwner: ViewModelStoreOwner, private val savedStateRegistryOwner: SavedStateRegistryOwner, private val ownerView: View // Used to post callbacks onto the UI thread. ): ViewAttachmentManager(context, ownerView), DefaultLifecycleObserver { private val windowManager: WindowManager private val windowLayoutParams: WindowManager.LayoutParams private val frameLayout: FrameLayout private val viewLayoutParams: ViewGroup.LayoutParams
init { windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager windowLayoutParams = createWindowLayoutParams() frameLayout = FrameLayout(context) viewLayoutParams = createViewLayoutParams()
frameLayout.setViewTreeLifecycleOwner(lifecycleOwner) frameLayout.setViewTreeSavedStateRegistryOwner(savedStateRegistryOwner) frameLayout.setViewTreeViewModelStoreOwner(viewModelStoreOwner)}
override fun onResume(owner: LifecycleOwner) { onResume() }
override fun onPause(owner: LifecycleOwner) { onPause() }
override fun onResume() { // A ownerView can only be added to the WindowManager after the activity has finished resuming. // Therefore, we must use post to ensure that the window is only added after resume is finished. ownerView.post { if (frameLayout.parent == null && ownerView.isAttachedToWindow) { windowManager.addView(frameLayout, windowLayoutParams) } } }
override fun onPause() { // The ownerView must be removed from the WindowManager before the activity is destroyed, or the // window will be leaked. Therefore we add/remove the ownerView in resume/pause. if (frameLayout.parent != null) { windowManager.removeView(frameLayout) } }
/**
- Add a ownerView as a child of the [FrameLayout] that is attached to the [ ].
- Used by [RenderViewToExternalTexture] to ensure that the ownerView is drawn with all
- appropriate lifecycle events being called correctly. */ override fun addView(view: View) { if (view.parent === frameLayout) { return } frameLayout.addView(view, viewLayoutParams) }
/**
- Remove a ownerView from the [FrameLayout] that is attached to the [WindowManager].
- Used by [RenderViewToExternalTexture] to remove ownerView's that no longer need to be
- drawn. */ override fun removeView(view: View) { if (view.parent !== frameLayout) { return } frameLayout.removeView(view) }
companion object { private const val VIEW_RENDERABLE_WINDOW = "ViewRenderableWindow" private fun createWindowLayoutParams(): WindowManager.LayoutParams { val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT ) params.title = VIEW_RENDERABLE_WINDOW return params }
private fun createViewLayoutParams(): ViewGroup.LayoutParams { return ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) }} } `
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. Thank you for your contributions.
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.