Why is my animation leaving a trail? - android

I'm animating a an ImageView from the left to the right of the screen using a translate animation. The ImageView is place inside a RelativeLayout over the top of my main layout using FrameLayout.
When I run the animation on the emulator everything works pretty well but when I use run it on my G1 it leaves visual artifacts behind and effects the rendering of the text component behind it.
Is this a performance issue and I'm being too ambitious or is it a bug I can overcome?
If it is a performance issue is there anything I can do to improve things?

I was also experiencing the same issue on 2.3.
Invalidating the container of the moving view ( the layout in which the moving view resides ) in Animation.applyTransformation fixed it for me.
See:
Android - Artifacts using Animation

I now this may be a little old, but I just found this:
http://groups.google.com/group/android-developers/browse_thread/thread/5481450f8b71a26c/e750730b9953d9a8?lnk=gst&q=animation+leaves+trails#e750730b9953d9a8
Not sure what android version your using, but it may be a bug in the android libraries!
Looks like that's what the problem is for me! :)
... Dontcha just love it when its not your fault! :D

Here's a workaround I found that solved this for me: "An easy workaround would be to pad your image with a small (1 pixel should do it) transparent region on the right/bottom - this would have no effect on how it would look, but it would force an invalidation of a region slightly larger than the actual image and thus compensate for the bug."
http://code.google.com/p/android/issues/detail?id=22151

Without actually seeing the problem is sounds like you're not clearing the display buffer before writing the next frame. It doesn't sound like a performance issue to me.
Do you have control over whether the device does double buffering or not?
Given that it works on the emulator this could point to either a problem with the emulator or a bug in your code that isn't showing up on the emulator (which I suppose is technically a problem with the emulator!) rather than a performance issue.

I would suggest using a SurfaceView for animation. It is double-buffered, so it should eliminate flickering if you use it properly. If you want an example, the LunarLander demo included in the sdk shows this really well. Also, if you have a more specific question with code, ask away.
As for general Android performance, it is very possible to have reasonably high frame rates, so you aren't expecting too much.

This is happening to me as well. I'm using an emulator using 1.6 with the Google APIs, and I just confirmed that it happens on a Nexus One running FRF83. Here's the relevant code:
Animation a = new TranslateAnimation(0.0f, 0.0f, 100.0f, 0.0f);
a.setDuration(2000);
this.myView.startAnimation(a);
Here's the relevant code for instantiating the view:
View v = new View(this.getApplication());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 80);
v.setLayoutParams(params);
v.setBackgroundColor(0xFFFF0000);
//
LinearLayout layout = (LinearLayout)this.findViewById(R.id.theLayout);
layout.addView(v);
//
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
// TODO Auto-generated method stub
doAnimation();
}
});
//
myView = v;
So basically, the double buffering etc, is being handled by the OS, and I have no control over it at all.

I had a similar problem on Android 2.3, so the bug may still in exist.
I was using an ImageView with a PNG which had some transparent parts. This imageview was leaving trails when animated with TranslateAnimation. Using a fake background drawable for the imageview elimanated the trail (I used a drawable as background).

I figured this out on Jelly bean i was experiencing this in a gallery view while performing some animation. It looks more like a drawing issue not cpu ....
make your activity implement the interface AnimatorListener ..... override the below method and pick which one you want to redraw your view in
#Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
mView.requestLayout();
Toast.makeText(this, "animation ended", 0).show();
}
#Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
mView.requestLayout();
}
#Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
mView.requestLayout();
}

Related

Using animation on a ViewPager and setFillAfter

I have a ViewPager which I need to move as a whole on button press. I use an animation for this.
When I press it, I translate the 'x' for it. I use setFillAfter(true) to keep the new position.
But when I change the page of the ViewPager, it jumps back to the original x-position!
I only saw this issue on Android 4.1, with Android 4.0 there is no problem! So it looks like some kind of regression in Android.
I attached a testproject where I could reproduce the issue without all my other stuff around it. I think it is best if you want to help me figure this out to import the project in your Eclipse and see it for yourself.
I also added to video's, one on my HTC One X where I see the issue, and the other on a tablet with Android 4.0, where the issue is not there.
I have been desperately looking to fix this ugly side effect, but no luck till now...
(Sorry for the big movie files...)
Video of Android 4.0 without the side effect
Video Android 4.1 with the side effect
the project where you can reproduce the issue with
Edit:
I added the following:
#Override
public void onAnimationEnd(Animation animation) {
RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) myViewPager.getLayoutParams();
if (!i)
lp.setMargins(300,0,0,0);
else
lp.setMargins(0,0,0,0);
myViewPager.setLayoutParams(lp);
}
After that it stays at the correct position, but it 'flickers' quickly, like the animation is still showing at the end and when I change the margin, it still shows the offset it had after animation. Then it jumps to the correct position.
The main problem seems to be incorrect choice of animation type. You see, View Animation as a tool is not intended to be used with complex interactive objects like ViewPager. It offers only low-cost animation of the drawing place of views. The visual behaivior of the animated ViewPager in response to user-actions is undefined and should not be relied on.
Ugly flicks, when you substitute a "gost" with the real object are only natural.
The mechanism, that is intended to use in your case since API 11 is specialized property animator built in Views for optimized performance: ViewPropertyAnimator, or not specialized, but more versatile ObjectAnimator and AnimatorSet.
Property animation makes the View to really change its place and function normally there.
To make project, to use, say, ViewPropertyAnimator, change your listener setting to this:
btn.setOnClickListener(new OnClickListener() {
boolean b = false;
#Override
public void onClick(View v) {
if(b) {
myViewPager.animate().translationX(0f).setDuration(700);
}
else {
myViewPager.animate().translationX(300f).setDuration(700);
}
b=!b;
}
});
If you want to use xml configuration only, stick to |ObjectAnimator and AnimatorSet. Read through the above link for further information.
In case, you are anxious to support pre-Honeycomb devices, you can use Jake Warton's NineOldAndroids project. Hope that helps.
That's because the Animation's setFillAfter(true) doesn't actually change the position or any attributes of the View; all it does is create a Bitmap of the view's drawing cache and leaves it where the animation ends. Once the screen is invalidated again (ie. changing the page in the ViewPager), the bitmap will be removed and it will appear as if the View is returning to it's original position, when in fact it was already there.
If you want the View to retain it's position after the animation has finished, you need to actually adjust the View's LayoutParams to match your desired effect. To achieve this, you can override the onAnimationEnd method of the Animation, and adjust the LayoutParams of the View inside there.
Once you adjust the LayoutParams, you can remove your call to setFillAfter(true) and your View will actually stay where you expect it to.
Regarding the flicker issue:
I have encountered this issue before, and it stems from the possibility of the onAnimationEnd() call not syncing up with the next layout pass. Animation works by applying a transformation to a View, drawing it relative to its current position.
However, it is possible for a View to be rendered after you have moved it in your onAnimationEnd() method. In this case, the Animation's transformation is still being applied correctly, but the Animation thinks the View has not changed its original position, which means it will be drawn relative to its ENDING position instead of its STARTING position.
My solution was to create a custom subclass of Animation and add a method, changeYOffset(int change), which modifies the y translation that is applied during the Animation's applyTransformation method. I call this new method in my View's onLayout() method, and pass the new y-offset.
Here is some of my code from my Animation, MenuAnimation:
/**
* Signal to this animation that a layout pass has caused the View on which this animation is
* running to have its "top" coordinate changed.
*
* #param change
* the difference in pixels
*/
public void changeYOffset(int change) {
fromY -= change;
toY -= change;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float reverseTime = 1f - interpolatedTime;
float dy = (interpolatedTime * toY) + (reverseTime * fromY);
float alpha = (interpolatedTime * toAlpha) + (reverseTime * fromAlpha);
if (alpha > 1f) {
alpha = 1f;
}
else if (alpha < 0f) {
alpha = 0f;
}
t.setAlpha(alpha);
t.getMatrix().setTranslate(0f, dy);
}
And from the View class:
private int lastTop;
// ...
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// the animation is expecting that its View will not be moved by the container
// during its time period. if this does happen, we need to inform it of the change.
Animation anim = getAnimation();
if (anim != null && anim instanceof MenuAnimation) {
MenuAnimation animation = (MenuAnimation) anim;
animation.changeYOffset(top - lastTop);
}
// ...
lastTop = top;
super.onLayout(changed, left, top, right, bottom);
}
Crucero has it right about setFillAfter not adjusting params post invalidation. When the view is re-layed out (which'll happen the pass after it's invalidated), its layout params will be the ones that always applied, so it should go back to the original position.
And Jschools is right about onAnimationEnd. Strongly encourage you to step through the source code with a debugger, where you'll instructively discover that an update is made that affects the drawn position of the view after onAnimationEnd is fired, at which point you've actually applied the layout params, hence the flicker caused by doubled up offset.
But this can be solved quite simply by making sure you relayout at the right time. You want to put your re-positioning logic at the end of the ui message queue at the time of animation end so that it is polled after the animation but before laying out. There's nowhere that suggests doing this, annoyingly, but I've yet find a reason in any release of the SDK reason why (when doing this just once and not incorrectly using ui thread) this shouldn't work.
Also clear the animation due to another issue we found on some older devices.
So, try:
#Override
public void onAnimationEnd(final Animation animation) {
myViewPager.post(new Runnable() {
#Override
public public void run() {
final RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) myViewPager.getLayoutParams();
if (!someBooleanIPresume)
lp.setMargins(300,0,0,0);
else
lp.setMargins(0,0,0,0);
myViewPager.setLayoutParams(lp);
myViewPager.clearAnimation();
}
}

WebView fails to render until touched Android 4.2.2 [duplicate]

This question already has answers here:
Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy
(9 answers)
Closed 9 years ago.
I am developing an open-source browser called Lightning Browser, and I have run into some trouble with the most recent update to Android (4.2.2).
WebViews will fail to render completely until the view is touched. It only occurs on the latest Android version. On 4.2.1, the WebView rendered completely fine. I use my Nexus 7 for development and right after I received the 4.2.2 update, the browser stopped rendering. Other users experienced this as well, and it was confirmed multiple times to happen ONLY on 4.2.2. It is happening targeting API level 16 and 17, but I have seen a WebKit browser that targets API level 9 without this problem.
I have attempted to remedy this using the solution to a problem I found here on Stack Overflow (Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy). HOWEVER, setting the WebView's RenderPriority to high alone does not solve it... the only way to get it to render without being touched was to place the invalidate() command inside the OnDraw() method of WebView. This causes the WebView to re-draw continuously. This works (sort of), animations are smooth, the page loads very quickly, but it causes the WebView's performance to drop otherwise.
I have also seen this question (very similar to mine), but there is no good answer for it. Android WebView Fails to Completely Render Content Until User Interaction
By performance drop, I mean input. Text input lags, and the WebView itself cannot handle what is going on as well as before. Benchmarking the browser with the invalidate() method call drops Benchmarking performance by around 8%. I know benchmarks aren't everything, but it's telling me that the continuous drawing is straining the system and causing the system to ignore other tasks.
In Conclusion...
The WebView in Android 4.2.2 will not render until touched. The only way I know to fix this is to call invalidate() in the onDraw() method of WebView. This is bad for performance and I am looking for a different way to fix this.
The Magic Code (I'm currently using)...
class MyWebView extends WebView
{
#Override
onDraw(Canvas canvas)
{
invalidate();
super.OnDraw(canvas);
}
}
remove
invalidate();
and it doesn't render until touched.
Does anyone have a suggestions on how make the WebView render (other than what I've done)? By the way, this is my first question I've asked here on Stack, so forgive me if I haven't been clear or if I've done something wrong.
EDIT:
I found this question here about a similar issue and it was resolved, the problem is, I don't understand what the answer even means. If anyone could enlighten me it might help.
I get this error in logcat
E/chromium(1243): external/chromium/net/disk_cache/backend_impl.cc:2022: [0705/172030:ERROR:backend_impl.cc(2022)] Corrupt Index file
So I finally found the issue causing the WebView not to render. I am starting and stopping the animation of a drawable inside the WebViewClient of my WebView. This drawable is simply a refresh button that rotates when the page is loading... simple right?
Well in the non-fullscreen mode of the browser, the rotating drawable and the WebView are both children of the same parent, whereas in the fullscreen mode, the drawable becomes a child of the WebView. Somehow, by starting the animation when the page is loading and stopping it when it's done (in fullscreen), the application decides that the rotating drawable is what needs all the drawing power and the WebView never draws. When in fullscreen mode and the drawable becomes a child of the WebView, the WebView then has a higher rendering priority than the drawable and it draws fine.
The moral of the story is... WebViews like to be the highest priority view to be drawn. If there are other views that get greater priority, they won't draw correctly.
I'm no expert on animations, so I need to rethink how I'm animating the drawable now so as to not interrupt the WebView.
Hopefully that made sense. Thanks for reading.
Disable hardware acceleration on your manifest:
android:hardwareAccelerated="false"
please see the last answer by #Olivier on Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy , he triggers a couple delayed invalidates on WebView's OnTouchEvent rather than continuosly on onDraw ... In my case it worked because (most) of my problems were after a user touching webview and I changing some CSS in response. Of course it doesn't apply at all on the case of automatic / timeout'ed css
In my case it also helped to export a JavaScript function from Java to manually trigger invalidate with a delay, so if in JavaScript you more or less know where the disaster might happen you can manually trigger it, something like this inner class inside your WebView:
public class MyWebView extends WebView {
private class InvalidateExtension {
private Handler handler=new Handler(); // you might already have a handler
private Runnable mInvalidater=new Runnable() {
#Override
public void run() {
MyWebView.this.invalidate();
}
}
public void trigger(int delay) {
handler.postDelayed(mInvalidater, delay);
handler.postDelayed(mInvalidater, 2*delay); // just in case
handler.postDelayed(mInvalidater, 4*delay); // just in case just in case :)
}
}
/** Call this function on this view's init, BEFORE loading a page so it is available to JS */
private void call_me_on_init_to_enable_hack() {
addJavascriptInterface(new InvalidateExtension(), "Invalidater");
}
}
So, from JavaScript you can do:
Invalidater.trigger(100);
And play with the milliseconds value ....
Hope this helps someone !
There is a problem with the zoom scale when rotating the phone and that will cause this problem, parts or the whole page will not be drawn. To solve this override onScaleChanged in your WebViewClient implementation and invalidate the view. That should force a redraw when needed.
#Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
if (view != null) {
view.invalidate();
}
}

Making smooth animations

I have a property animation, but it's not smooth, i.e I'm getting jumps in the animation. I tried to play with the parameters for the animator, such as the interpolator, the duration, and the frame delay, but can't get a smooth effect. Does anyone have some tricks / examples? Here's my current code for setting the animator:
rotationAnimator=new ObjectAnimator();
rotationAnimator.setTarget(rotationController);
rotationAnimator.setPropertyName("mapRotation");
rotationAnimator.setInterpolator(new LinearInterpolator());
ValueAnimator.setFrameDelay(24);
rotationAnimator.setDuration(ROTATION_DURATION);
rotationAnimator.setRepeatCount(ValueAnimator.INFINITE);
rotationAnimator.setRepeatMode(ValueAnimator.RESTART);
(There's also a call to setFloatValues, but it comes later in the code, before I start the animation)
EDIT: I was asked to show what I'm animating, so here it is:
That's the setter function that gets the value from the animation:
public void setMapRotation(float rotationDegrees)
{
if ((rotationAnimator!=null)&&(rotationAnimator.isRunning()))
rotationAnimator.cancel();
rotationDegrees%=360;
if (rotationDegrees<0) rotationDegrees+=360;
rotationView.setRotationDegrees(rotationDegrees);
if (rotationDegrees!=oldRotationDegrees)
{
notifyRotationListeners();
oldRotationDegrees=rotationDegrees;
}
}
If you look at the code, you'll see there's another function that gets called (setRotationDegrees), so here it is:
public void setRotationDegrees(float rotationDegrees)
{
this.rotationDegrees=rotationDegrees%360;
if (this.rotationDegrees<0) this.rotationDegrees+=360;
Log.i("MapView","View degrees: " + this.rotationDegrees);
invalidate();
}
And that's what happens after the invalidation:
#Override protected void dispatchDraw(Canvas canvas)
{
Log.i("MapView","Redrawing");
int saveCount=canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(rotationDegrees,getWidth()/2,getHeight()/2);
canvas.setDrawFilter(drawFilter);
canvas.getMatrix(rotationMatrix);
super.dispatchDraw(canvas);
canvas.restoreToCount(saveCount);
}
I don't know if there's something particularly heavy here, but I may be wrong...
Defining an AnimatorSet and set the Interpolator on it may solve your issue:
rotationAnimator=new ObjectAnimator();
rotationAnimator.setTarget(rotationController);
rotationAnimator.setPropertyName("mapRotation");
ValueAnimator.setFrameDelay(24);
rotationAnimator.setDuration(ROTATION_DURATION);
rotationAnimator.setRepeatCount(ValueAnimator.INFINITE);
rotationAnimator.setRepeatMode(ValueAnimator.RESTART);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.getChildAnimations().add(rotationAnimator);
animatorSet.setInterpolator(new LinearInterpolator());
...
animatorSet.start();
I've resolved by adding HW acceleration directly to my animated view and view container (myViewContainer - in my case a RelativeLayout):
if(!myViewContainer.isHardwareAccelerated())
{
myViewContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
myAnimatedView1.setLayerType(View.LAYER_TYPE_HARDWARE, null);
myAnimatedView2.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
Jumps in the middle of the animation are more likely to be caused by either something in your rotationController class, that is receiving the animated value "mapRotation". Or it could be something to do with your layout.
I would imagine that there is something quite intense, memory or cpu wise, that is causing the juttering.
If your animating something that has lots of layouts then that could be the cause. If in your code you have lots of findViewByID that is getting called as a result of your animation then that to can cause a slow down.
If your just animating a simple image, then im not entirely sure what the issue would be.
Personally i think your code looks fine, perhaps show/describe what your animating.
[edit]
I have limited knowledge of animating with the canvas so i took a quick look at how you were using canvas.save() and canvas.restoreToCount() which i hadn't seen before.
It all looks good though i do find it odd that several tutorials rotate the canvas rather than the ball image to create the rotation. Still, the only reference i found to improving the animating was a comment in the following link that suggested using postInvalidateOnAnimation(). Not sure where though, perhaps instead of invalidate().

Entitymodifiers not working (AndEngine / Android)

I'm playing around with AndEngine. No docs available for reading, so I'm just shooting in the dark here.
Finally got a splash screen showing. Now I'm trying to add some transitions to it, but no luck here. Here's the code:
#Override
public void onLoadComplete() {
mHandler.postDelayed(fadeAway, 2500);
}
protected Runnable fadeAway = new Runnable() {
#Override
public void run() {
// The only child of the scene is our splash sprite
scene.getLastChild().registerEntityModifier(new SequenceEntityModifier(
new ScaleModifier(2500, 100.0f, 200.0f),
new RotationModifier(2500, 0.0f, 78.0f),
new AlphaModifier(2500, 1.0f, 0.0f)
));
}
};
What happens is that the postDelayed() runs fine (waiting for 2.5 secs), but then everything goes black immediately. What I was expecting was that the splash screen should zoom in to 200%, then rotate 78 degrees, then fade out, but since everything goes black it feels that the duration of the modifiers is not working.
Is there an apparent error here?
EDIT: Alright, found the errors: 1) Apparently the pDuration (first argument) is supposed to be in seconds, not milliseconds as everywhere else 2) In ScaleModifier(), 1.0f equals the original size, so that argument is not in percent as expected.
(No flame, but I'm truly amazed how people managed to learn how to use this library without any documentation. There's not a single comment or note in the whole source code. Did people trial-and-error-reverse-engineered everything to find out how it's supposed to work? Can't believe the author put this vast amount of work for this library and never supplied any docs.)
Your errors is listed below:
All durations in AndEngine are in seconds.
Normal scale is 1.0f not 100.0f.
There is no need for Android Handler. You should delay your works in onUpdate methods of the engine or other AndEngine stuff.
You should apply animations only in update thread not anywhere else. mEngine.runOnUpdateThread(...)

How does Android's setDrawingCacheEnabled() work?

I have a UI where the root layout is a RelativeLayout. It has a number of Views, such as fields, buttons, etc.
There are also two other panels that are initially invisible. When the user clicks a button, one of these panels slides in from the left, another slides in from the bottom. The problem is the frame rate is pathetic on a Nexus S.
I want to use setDrawingCacheEnabled(true) in order to (I hope) speed up the animation of these two panels. As a simplified example, here is roughly how I slide in one of these panels. I'll call it "detailPanel".
public class MyActivity extends Activity {
// Initially invisible.
private View detailPanel;
// A simple slide animation.
private Animation detailEnterAnimation;
...
public void someButtonClicked(View view) {
detailPanel.startAnimation(detailEnterAnimation);
detailPanel.setVisibility(View.VISIBLE);
detailPanel.setDrawingCacheEnabled(true);
}
}
Now in the DetailPanel I try to use the cache. I was hoping that bitmap rendering via the cache would improve performance:
public class DetailPanel extends LinearLayout {
public void draw(Canvas canvas) {
Bitmap cache = getDrawingCache();
if (cache != null) {
canvas.drawBitmap(cache, 0, 0, null);
} else {
super.draw(canvas);
}
}
}
The Bitmap is non-null and is the right size, but it is completely black.
Why is my bitmap black? That code may be obscenely wrong; I've tried a million different things and just cannot figure it out.
From Android documentation (http://developer.android.com/reference/android/view/View.html#setDrawingCacheEnabled%28boolean%29):
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. If you want to use a Bitmap for the view, even when hardware acceleration is enabled, see setLayerType(int, android.graphics.Paint) for information on how to enable software and hardware layers.
Maybe this is your case?
You may want to build your drawing cache before using it, otherwise It will give you a blank bitmap
public void someButtonClicked(View view) {
detailPanel.startAnimation(detailEnterAnimation);
detailPanel.setVisibility(View.VISIBLE);
detailPanel.setDrawingCacheEnabled(true);
detailPanel.buildDrawingCache(); //may be you need to add this?
}

Categories

Resources