The Android View Class has a method called onDraw, and onDraw gets passed a canvas. Only the user never explicitly creates the canvas that gets passed and appears to have no other means by which to access it.
What is the canvas that gets passed to onDraw, where is it created and is there a way to access it?
Thank you for your help.
As concluded from Understanding Canvas and Surface concepts
A view implicitly has a canvas associated with it. When invalidate() is called from within a specific view, or for a specific view (i.e. View v and you then call v.invalidate()) the canvas associated with that view is sent to the View's onDraw method.
When a view contains other views, the hierarchical view tree is traversed and redrawn starting from the view that made the invalidate call.
ViewRootImpl, the topmost class in the view hierarchy, requests a Canvas for the entire visible window, and passes it to the top level ViewGroup of your Activity. Each ViewGroup then passes a concatenatedsubsetted version of this Canvas to each child View's draw() method.
As far as I know, it's not possible to manipulate the Canvas outside of onDraw() (and related methods).
You might try calling draw() yourself with a new instance of Canvas that you control. That won't draw anything onto the screen, but you'll be able to read the Canvas elsewhere in your class, convert it to a bitmap, etc. See this answer for an example: Convert view to bitmap on Android
Related
I want to design a gauge View in Android, for example a round thermometer with a rotating needle.
For efficiency / speed, when new data arrives I'd like to re-draw only the parts of the View that change (e.g. the rotating needle).
My initial test class extended View, and I had to re-draw the entire view (moving and non-moving elements) in onDraw() each time new data arrived.
I found an example (http://mindtherobot.com/blog/272/android-custom-ui-making-a-vintage-thermometer/) that suggests drawing the non-moving components onto a Bitmap. This Bitmap is drawn in onDraw() using canvas.drawBitmap. In this way you only need to draw the individual non-moving graphic elements (regenerate the Bitmap) when something changes the non-moving parts of the View (e.g. resizing the View).
My idea for optimization was different. I think perhaps my gauge should extend ViewGroup rather than View. Then within the ViewGroup I could create individual Views for the non-moving and moving parts of the gauge. I would draw the non-moving View once, and never force it to invalidate(). Then I would draw the moving needle as a separate View (aligned in the ViewGroup such that it overlays the non-moving View) which I could invalidate() when new data arrives, so it would be re-drawn. Am I missing something that makes this a bad way to do it?
For example, is my assumption incorrect that the non-moving background View will not need to be re-drawn (i.e. have its onDraw called) as the overlaid rotating-needle View in the same ViewGroup is changed? Or does having the needle View change force the background View in the same ViewGroup to get re-drawn as well, due to the Views overlapping?
Many thanks.
I read about the invalidate overload that takes a rectangle, but I didn't understand how I can determine that rectangle when onDraw is called (if I have a custom view and implement onDraw). Do I have to keep it in a member variable, so I will have access to it and then redraw only that rectangle, or am I supposed to ignore it altogether and redraw everything, letting Android handle it so that only the rectangle is actually refreshed?
Thanks.
canvas.getClipBounds();
Additionally, anything you do in onDraw outside the invalidated region is ignored.
Should I take view translation into account when overriding onDraw()? Or it is already applied to canvas translation?
I.e. should I execute
canvas.translate(getTranslationX(), getTranslationY());
in first line of onDraw()?
Do all transformations applied to canvas in onDraw() call persist in subsequent calls? I.e. should I suppose canvas is each time untransformed?
No, you do not need to do this. A View's translation is applied before onDraw is called.
As HardCoder points out, state changes that you make to the Canvas passed to onDraw will not persist to the next call to onDraw.
As far as I know the canvas is not persistant therefore you should execute the translate. However you can save the canvas and restore it:
http://maohao.wordpress.com/2009/09/30/canvas-save-canvas-restore/
Android View.onDraw() always has a clean Canvas
http://blahti.wordpress.com/2010/12/20/moving-views-in-android-part-1/
I am currently developing a single view android app using the simple invalidate/onDraw strategy, and have noticed that the canvas always appears erased prior to the invocation of the onDraw method. Does anyone know of a simple way to preserve the current canvas contents prior to the onDraw method call? Any help would be most appreciated.
Use a bitmap to store the last drawn state and in every onDraw method call, draw the bitmap to the canvas before anything else.
I've implemented a custom view, in my view I display another set of views (carousel) while drag I apply different effects to child views like scale, alpha etc... to achieve this I get drawing cache of view and do required transformation to the bitmap matrix.
I've found that getDrawingCache() method is slow enough, what I am looking for is an alternate approach.
How is it possible to transform some view without touching it's drawing cache?
getDrawingCache() is as fast as View.draw(). It creates a Bitmap if necessary and calls View.draw() to render into this bitmap. There's nothing inherently slow about getDrawingCache() (except the allocation of the Bitmap itself) so I suggest you profile your code to understand why it takes so long to render your views.
It could also be that you are somehow calling invalidate() on the Views on every frame, causing getDrawingCache() to redraw the View every time.