Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient "Screenshots" of a View?

Tags:

android

TL;DR: Since getDrawingCache() seems to trigger a complete redraw of a View when hardware acceleration is enabled, is there an alternative means of getting a Bitmap (or something along those lines) that avoids this, perhaps by reading data populated into a (hardware, software) layer when the View was last drawn?


Some background:

Android has had the ability to mirror the screen since Android 3.0, such as to an attached HDMI display. This can be used for presentations, but it means the audience sees the same thing as the presenter, which is not always ideal.

Android 4.2 added Presentation, to allow apps to put arbitrary stuff on the "second screen" (e.g., HDMI attached display). In this case, at times, it would be useful for the second screen to be showing part of what is on the main tablet's display. If you think of presentation software like Microsoft PowerPoint, LibreOffice Impress, and the like, in typical dual-screen setups, the audience sees the current presentation slide, while the presenter sees the current slide and a timer and speaker notes and...

For non-interactive content, like a PNG representing a slide, this is simply a matter of showing the same image in both screens (alongside other stuff on the primary screen).

However, for interactive content, like a WebView, it is sometimes difficult to do this sort of mirroring. For example, we have no good way of knowing when the content of a WebView might change, as it may do so based on stuff purely within the WebView itself (e.g., completion of AJAX calls), not something we do separately. And, even if we did know when the content of the WebView changed, we have no good way of getting some other WebView to render that same content.

So I figured I'd try to set up a MirroringFrameLayout, that would use getDrawingCache() to retrieve a Bitmap of the container's contents and deliver that to somebody who can render it on-screen (e.g., an ImageView shown in a Presentation).

However, with hardware acceleration enabled, setDrawingCacheEnabled(true) is somewhat of a no-op:

Enabling the drawing cache is similar to setting a layer when hardware acceleration is turned off. When hardware acceleration is turned on, enabling the drawing cache has no effect on rendering because the system uses a different mechanism for acceleration which ignores the flag.

Calling getDrawingCache() in these cases forces a full draw() of the View to a bitmap-backed Canvas, rather than actually using a cache. Since draw() may be expensive, doing draw() frequently (say, triggered via postOnAnimation()) results in jank.

Hence, I am trying to determine if there is some other "drawing cache", beyond getDrawingCache(), that we can use with hardware acceleration enabled, that could be used to set up this mirroring, and that is more efficient. From what I can see, there is no such cache, as layers are effectively write-only from the standpoint of SDK apps. However, I am hoping that perhaps I am missing some solution.

Thanks in advance!

like image 316
CommonsWare Avatar asked Mar 21 '13 15:03

CommonsWare


1 Answers

You can use setLayerType(View.LAYER_TYPE_SOFTWARE, null) but it will have the side effect of making that View slower to redraw every time it updates. It would be much more efficient to create a custom ViewGroup that draws your View on two Canvas (one for each screen.) You could also simply use a ViewTreeObserver and on every draw callback force your view to render on the second screen. I really recommend you don't try to abuse the drawing cache/layer type for such a use case.

like image 161
Romain Guy Avatar answered Oct 18 '22 14:10

Romain Guy