I need to create a custom view with animation. I extend View class and override onDraw. This custom view has some method, which when called must redraw with animation canvas(inside onDraw) part. I make the animation via ValueAnimator, inside onAnimationUpdate where I calculate the animation offset and call invalidate(). It works ok, but I must redraw the whole view, the animated part and the rest. I try use
invalidate (int l, int t, int r, int b)
but in this case the animation does not work. onDraw called only twice.
Is it possible to redraw only the canvas part, or must I redraw everything every time?
Is it good practice to use ValueAnimator for canvas animation?
Yep, basically you can redraw just a part of the view using the method that you are describing. However, if you are animating using a ValueAnimator, the overhead of redrawing the whole view will be negligible and you will avoid problems in your animations related to "dirty" parts of the view not getting redrawn.
simply call invalidate without arguments.
P.S.: If it gives you some peace of mind, just open the profiling tools and watch them when the animation is run. In normal circumstances you won't even be able to see a reaction.
Related
I have read about Property Animation and Hardware Acceleration but I am still uncertain what is the most efficient way to use the animator classes. (For the sake of this question I don't need to support devices before Honeycomb. So I want to use the animator classes.)
For example, say I have a View. In this view I have a BitmapDrawable that I want to fade in. There are also many other elements within the view that won't change.
What property or object would be best to use with the animator? The drawable? A paint that I am drawing the bitmap with in onDraw? Something else?
How can this be done to be most efficient with hardware acceleration? Will this require calling invalidate for each step of the animation or is there a way to animate just the drawable and not cause the rest of the view to be redrawn completely for each step of the animation?
I guess I imagine an optimal case would be the rest of the view not having to be completely redrawn in software, but rather hardware acceleration efficiently fading the drawable.
Any suggestions or pointers to recommended approaches?
Thanks!
With the use of the Object Property Animators, basically they're just math functions that repeatedly call a "setN()" method every X miliseconds where "N" is the property you want to change.
In the example provided in the case of alpha, both would require a call to invalidate() to redraw the View that you are animating. The difference being when you call setAlpha() on the View object, it calls invalidate() for you. If you were to set the target Object to the Paint object that is used to draw the drawable, you would still need to call invalidate() on the View so it will redraw with the new Paint parameters.
Ideally you want to set the target to the highest level child you can so the redrawing only happens on the views you want to animate. If you set the target to the root View, for example, it will call invalidate() on every child in the entire ViewGroup which will in turn call draw() on every child in the ViewGroup. If you set it to a top level ImageView, then only ImageView will be redrawn.
To best utilize the Hardware, you need to use the Layer properties. First, you need to decide what the top-most parent View you want to animate will be. If you want to only fade the Drawable, then it will be the Drawable or containing View. If you want to fade everything, then it will be the root view. Whatever you decide to animate will be applied to the View as a whole at once.
Use setLayerType() on the parent view just before you start the Animator. Set it to View#LAYER_TYPE_HARDWARE. Then set an AnimationListener and reset the LayerType to View#LAYER_TYPE_SOFTWARE or View#LAYER_TYPE_NONE upon completion of the animator.
myParentView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
myObjectAnimator.addListener(new ViewAnimator.AnimatorListener() {
public void onAnimationEnd(Animator animation) {
myParentView.setLayerType(View.LAYER_TYPE_NONE);
}
public void onAnimationRepeat(Animator animation) {
}
public void onAnimationStart(Animator animation) {
}
public void onAnimationCancel(Animator animation) {
myParentView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
myObjectAnimator.start();
In this case, if you were to translate myParentView by using the translateX property, then it will put layer myParentView and all it's children into one plane. Put it in to hardware memory. Translate the entire view all at once. Then, upon completion, remove myParentView from memory.
EDIT:
One final note, Alpha wreaks havoc on the CPU. If you have something on half-alpha and translate it across the View, it will be harder to render than if you simply translate the View. Use it sparingly.
Perhaps, you can overlay a new view(which contains the animator only) on your original one. the new view set to Transparent.
the reset you should do is invalid the new view without your original view.
After going through your queries I will suggest please go through this standard document which is neat to make you understand how to use invalidate() properly. Secondly, there are different ways already provided by android API to work with animation in different situations.
Here, I hope most of your doubts will be cleared. Please go through the sub-sections and related blog mentioned their.
Hope this will help you.
I have two Android OpenGLSurface views next to each other and I'd like to render to a portion of the view offset from the center.
Basically what I want to do is call glViewPort(x,y,width,height) twice with different x for each SurfaceView.
For some reason the glViewPort call gets applied to both surfaceViews though even though I'm using different GL10 instances.
How do I fix this?
I ended up simply calling glViewPort, then rendering the first view, and then calling glViewPort for the second view, and then rendering the second view. This fixed the issues.
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.
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.
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.