Status Update
Comments
nj...@google.com <nj...@google.com> #2
One detail that might assist with this investigation is that Views will automatically discard their own displaylist when they are detached from the window. So in this case it might be more applicable to have the View draw directly into the canvas provided to it rather than draw it's own RenderNode instance. That would ensure that the GraphicsLayer's displaylist has the contents of the View independently of it's own internally managed RenderNode.
nj...@google.com <nj...@google.com> #3
More notably, this hidden protected method might be helpful in ensuring that the View draws into the provided Canvas rather than View's internal RenderNode:
/**
* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
*
* If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't
* HW accelerated, it can't handle drawing RenderNodes.
*
* @hide
*/
protected final boolean drawsWithRenderNode(@NonNull Canvas canvas) {
return mAttachInfo != null
&& mAttachInfo.mHardwareAccelerated
&& canvas.isHardwareAccelerated();
}
If this method returns false, the GraphicsLayer will capture the drawing commands of the View rather than just drawing the View's RenderNode. So even if the View discard's it's own displaylist the GraphcisLayer would persist the drawing commands just like any other Composable. If the View is detached from the window, there's not much else that can be done to persist its RenderNode's displaylist aside from this.
an...@google.com <an...@google.com> #4
There are good reasons why each View wants to have its own RenderNode - it allows to redraw each view separately from the rest of hierarchy, without that even small and localized redraw will require the whole Views hierarchy to be fully redrawn. Simple example is having a RecyclerView be a content of AndroidView composable. Now, when each view has its own RenderNode, for each scroll event of RecyclerView we don't need to redraw the children, instead we just offset their RenderNode's positions. If we somehow disable it, each scroll will require way more work to redraw everything ruining the performance.
In the bug description I proposed a different approach: we might need to alter the AndroidView's lifecycle. We will call onRelease() and detach the views only when the parent layer discards its content, I believe this option might be more appropriate for the task, but worth exploring if there are other options on the table.
Description
Simple example would be:
After the flag switched to false, the text drawn by the Text composable will continue to be displayed, but the text drawn by the TextView is not.
The main side effect of it is we don't support disappearance animations via Modifier.animateItem() for the View based content in all the lazy layouts.
This issue was discussed during the design of the list item animations in go/compose-list-item-animations - Issue C, Option 2. Quote from there:
"We will also need some adjustments on how we handle AndroidViews inside items. It is especially important for TextureView or SurfaceView (for example video players). Once we remove a View from the views hierarchy its surface is destroyed. We can solve it by keeping views attached until the animation finishes. We already introduced the AndroidView(onRelease = {}) callback which should be used by the users when the view is fully released. And we already keep views after the AndroidView is disposed when we reuse them during scrolling. In this case we will continue using the same already provided callbacks and will call onRelease only after the animation finishes."
As part of this ticket we need to make this use case work. It will likely require: