Android getDrawingCache() is slow - android

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.

Related

Does every view have its own canvas/bitmap to draw on?

I have a relativelayout with three full screen child views of the same custom view class. I am wondering whether I should be worried about memory. Judging by this answer:
Understanding Canvas and Surface concepts
All views get passed the same canvas with underlying bitmap to draw on so that the memory is not tripled. Can anyone confirm?
It would make sense, otherwise a full screen textview would be very inefficient.
Bonus: is the purpose of canvas to define a drawable area of a bitmap and translating the view coordinates to bitmap coordinates?
As per the documentation http://developer.android.com/guide/topics/graphics/2d-graphics.html#draw-with-canvas:
When you're writing an application in which you would like to perform specialized drawing and/or control the animation of graphics, you should do so by drawing through a Canvas. A Canvas works for you as a pretense, or interface, to the actual surface upon which your graphics will be drawn — it holds all of your "draw" calls. Via the Canvas, your drawing is actually performed upon an underlying Bitmap, which is placed into the window.
In the onDraw(Canvas canvas), you are given a canvas object. This canvas has an underlying bitmap. All views are not given the same canvas. Canvas is just a layer above the common bitmap (which is pixels on the screen). canvas offers you to manipulate the bitmap as much as you want. So every view has a canvas, but not it's own bitmap.
So no, as far as memory is concerned, three view doesn't mean memory is tripled, because there is just one bitmap. You could however create your own bitmap, if you do so, then you will be jogging up the memory. If you create 3 bitmaps with size of the screen, your memory will be tripled.

Update view's canvas outside of onDraw on Android

I need update a small portion of a custom view in order to display a small animation. The rest portion of the view has only static image. The most straightforward would be to obtain the canvas of the view and update only that particular portion directly. But I can't really find anyway to get the view's canvas object outside of the view::onDraw method.
The only alternative I know is this: call view::invalidate() with a specified rectangle to minimize the drawing flicker. I have the code to update the entire view within onDraw. So the best thing to do is to detect the clipping rect and only run the code to update the specified area, in order to minimize CPU usage as well?
I guess I will try to answer this question myself to the best my knowledge so far.
There is no direct access to the canvas outside of the onDraw method.
Although we can detect the clipping rect with the function Canvas.getClipBounds(), the getClipBounds function always return the entire view area if GPU is enabled. When GPU is not used, getClipBounds() returns the actual dirty area. Since there is a GPU in most phones, it makes the function getClipBounds pretty much useless.

Smooth scrolling on Android with slow onDraw

I have a big custom View, and I draw a lot of rich text on it, many fonts, multiple colors and font sizes. I already optimized this drawing as much as possible. The redraw takes about 300ms.
This 300ms is OK for one time call, but since my View is bigger than the screen, it is inserted into the ScrollView, and now once the user scrolls with the finger, onDraw() is called repeatedly. Which gives very sloppy scrolling.
The same application was previously written for iOS, and iOS will not call onDraw (I think it is called drawRect() in iOS world), and all the drawn image is stored in some sort of buffer, so scrolling actually is very fast and ultra smooth, probably hardware accelerated.
So is there a way to achieve something like "No onDraw" smooth scrolling? I was thinking about drawing onto bitmap, and then blitting the bitmap during the scroll phases. But will it be any faster than drawing text? Are bitmaps HW accelerated on Android? What is the best approach to this kind of problem?
Thank you
Make the entire drawing into a Bitmap object using associated Canvas and in onDraw just flush this Bitmap. When you data changes, redraw everything into Bitmap.

Scrollview Optimizations

I have a scrollview that contains a custom view. The custom view is bigger than the area of the screen and draws properly.
The scrollview however tends to call onDraw() non-stop when scrolling, and I can't seem to make it smooth.
I used ScrollView.getDrawingRect() to calculate the visible portion of the screen and only draw to that, but it still returns the entire viewport (so it's optimized to not draw offscreen areas), and not the delta between the last position and the current one. Ideally I'd want to draw only the delta, and not the entire visible window.
If anyone can point me to more information about how to use the drawing caches, and if that will help optimize scrolling, I'd love to implement it, or any other possible solutions it would be greatly appreciated.
When content is scrolled, the entire viewport needs to be redrawn because all of the content has moved. I don't think there's anything that needs to be done to optimize a ScrollView - if scrolling is slow then it's the drawing method of your custom view that is too slow.
Try to avoid object creation in your draw methods which is usually the main culprit for poor drawing performance.
Edit: Also the scrollview could blit the old content up or down quickly that is still drawn on the screen, and then request a redraw of only the "new" portion of the screen. (only applies to opaque views).
I encountered the same problem. I solved it by using the function setDrawingCacheEnabled(true). By enabling this setting, your canvas view will be cached as bitmap, so you don't have to call canvas' draw method each time onDraw() is called.
In your custom view's constructor, you will need something like this:
public CustomView(Context context) {
setDrawingCacheEnabled(true);
drawnFlag = false;
}
In your onDraw method, you will need something like this:
public void onDraw(Canvas canvas) {
if (! drawnFlag) {
canvas.drawPath(...);
canvas.drawPath(...);
drawnFlag = true;
}
}
Now, scrolling on this custom view should be smooth since we only call the drawing methods once.
Afaik the ScrollView sets a proper clip rect on the canvas your view gets in onDraw so you only need to draw what's inside that rect.
You could also implement cache bitmaps based on the clip rect's size.

Android - drawing cache - when is it useful?

I am reading about setDrawingCacheEnabled and getDrawingCache and I was wondering when is it good to use it or when its not good.
Basically in my case I have an HorizontalScrollView with many things inside it so its scrolls left/right and most of the things are not visible.
If I use setDrawingCacheEnabled(true) on the views, does it help? or this is only when I use custom views and I call getDrawingCache()?
Is there any other 'cache' way to use in a HorizontalScrollView?
TouchInterceptor.java - This is class responsible for reordering your playlist in the default music player. It uses setDrawingCacheEnabled when you start dragging the current view. Basically, it creates a bitmap from the ListView item and drag it. Take a closer look at onInterceptTouchEvent method.
It's definitely useful for screenshots as Marcel said. It is also very useful performance-wise, as that is what it was created for. It does use up more memory, as you render the view into a bitmap first.
What you do is, you setDrawingCacheEnabled to true, call getDrawingCache which returns a bitmap and store this bitmap. In onDraw, you do draw the bitmap you got if the cache is on, or the view otherwise. This can be very nice when scrolling.

Categories

Resources