As we know , the android coordinate system is start from the top left corner of the android screen. The x-axis is down growth and the y-axis is right growth.But I found it's not right for the animation.
For example, I initialized the TranslateAnimation using the constructed function:
TranslateAnimation ta = new TranslateAnimation(0.0f, 200, 0.0f, 200);
Does the coordinate system have changed ? I found it didn't start from the top left corner.
Then I initialized the other translateAnimation for moving up and right direction :
TranslateAnimation ta = new TranslateAnimation(0.0f, 200, 0.0f, -200);
ta.setReaptMode(Animation.REVERSE);
The same behavior would be found.
I am confused about it.
I believe that constructor for TranslateAnimation uses deltas. See this. Or look at the constructor sig. : (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta). So if you want your anim. to jump up first, you could use a negative third ctor param.
More precisely:
An animation can never start until after the layout has been measured. One usually shouldn't have to worry about how this works beyond that the algorithm is mostly very good and you can take control of its strategies by setting layout parameters. In short, by the time an animation might be started, we know where you want the view to be on the screen, because you set layout parameters.
Translate animation then takes deltas from that position. So your current animation shouldn't start from the top left, but rather wherever those layout params were evaluated by onMeasure.
Some would say- how annoying. It's gonna get complicated even if you just want to do some simple up-down type animations... Well, here's an advisable development strategy; it snould make android animation development a breeeze. Set an animationListener on every animation. In onAnimationEnd, in possibly a parametized way, reset the layout parameters on the view your animating to where you expect it to be. That way, you'll get no surprising "jumps" when you re-apply an animation again. You may need to invalidate in some circumstances, or clearAnimation. The reason that this works is that the measure pass will be caused to come round again and you'll have a new offset for your TranslateAnimation. Finally, you may want to do all this resetting posted to the message queue of a view using post(Runnable runnable) in the listener, so you're off the last pass of the animation draw.
I too found android Animations can occasionally surprise you and cause jumpy behaviour. But if you do it like this, constructors taking delta params shouldn't be confusing again.
Related
I'm trying to move a view to the upper right corner of the screen, using ObjectAnimator.ofFloat(...) But, I'm not getting the results I expect. I'm getting the coordinates of the view beforehand, using ViewTreeListener, etc, and I already know the x value I need to offset from the end of the overall width. I can't get either dimension to move to where I want it. Relevant code:
Getting the starting coordinate; where the view currently is:
int[] userCoords = new int[]{0,0};
userControlLayout.getLocationInWindow(userCoords);
//also tried getLocationInScreen(userCoords); same result
userUpLeft = userCoords[0];
userUpTop = userCoords[1];
Surprisingly, I get the same value as userUpLeft (which is in screen coordinates, and not relative to parent) when I call userControlLayout.getLeft() I'd expect them to be different per my understanding of the docs. Anyway...
Constructing the ObjectAnimators:
//testXTranslate is a magic number of 390 that works; arrived at by trial. no idea why that
// value puts the view where I want it; can't find any correlation between the dimension
// and measurements I've got
ObjectAnimator translateX = ObjectAnimator.ofFloat(userControlLayout, "translationX",
testXTranslate);
//again, using another magic number of -410f puts me at the Y I want, but again, no idea //why; currently trying the two argument call, which I understand is from...to
//if userUpTop was derived using screen coordinates, then isn't it logical to assume that -//userUpTop would result in moving to Y 0, which is what I want? Doesn't happen
ObjectAnimator translateY = ObjectAnimator.ofFloat(userControlLayout, "translationY",
userUpTop, -(userUpTop));
My understanding is that the one arg call is equivalent to specifying the end coordinate you want to translate/move to, and the two arg version is starting at...ending at, or, from...to I've messed around with both and can't get there.
Clearly, I'm missing very fundamental knowledge, just trying to figure out what exactly that is. Any guidance much appreciated. Thanks.
First, userControlLayout.getLeft() is relative to the parent view. If this parent is aligned to the left edge of the screen, those values will match. For getTop() it's generally different simply because getLocationInWindow() returns absolute coordinates, which means that y = 0 is the very top left of the window -- i.e. behind the action bar.
Generally you want to translate the control relative to its parent (since it won't even be drawn if it moves outside those bounds). So supposing you want to position the control at (targetX, targetY), you should use:
int deltaX = targetX - button.getLeft();
int deltaY = targetY - button.getTop();
ObjectAnimator translateX = ObjectAnimator.ofFloat(button, "translationX", deltaX);
ObjectAnimator translateY = ObjectAnimator.ofFloat(button, "translationY", deltaY);
When you supply multiple values to an ObjectAnimator, you're indicating intermediate values in the animation. So in your case userUpTop, -userUpTop would cause the translation to go down first and then up. Remember that translation (as well as rotation and all the other transformations) is always relative to the original position.
I have a mixture of animations running on various scenarios.
As an example;
I have a fade-in and fade-out animator on a small circle bitmap. On ACTION_DOWN the fade-in should start
and on ACTION_UP, when the fade-in ends, fade-out should run. BUT, if the user picks up the finger before the fade-in ends, it should stop right there (fadein.cancel()), fade-out should start from THAT alpha value.
I dont want a new object animator each time, I defined an animator like this in the constructor
fadein = Objectanimator.ofFloat(bitmap, "alpha", 1.0f);
fadeOut = Objectanimator.ofFloat(bitmap, "alpha", 0.0f);
What happens is, if I produce the issue as mentioned (picking up the finger fast), the fadeout animator will pick up the alpha value where fade-in left off and it'll RETAIN that. That is, 2nd time onwards it'll NOT pick up a new alpha! Why is that? Whats the workaround? Create new objects each time?
ObjectAnimator class modifies the attributes of the view, so it updates the property of the view using the setter method of the Object property (as per official Android Guide).
So, when fadein animation runs, the alpha value is constantly modified, and when fadein.cancel() executes, the alpha value of the View stops updating. Let's say this value is X.
So, when you start fadeOut, it animates from X to 0.0f.
Whats the workaround?
For fadeOut, use two values imply a starting and ending values
fadeOut = Objectanimator.ofFloat(bitmap, "alpha", 1.0f, 0.0f);
So if I have a translation animation like
Animation anim = new TranslateAnimation(0, 0, 0, OFFSET);
anim.setDuration(1000L);
anim.setAnimationListener(listener);
anim.setFillAfter(true);
and I apply this to a textswitcher so that given a offset, the textswitcher will move from a location, A, up or down.
My textswitcher will stay at a given location, B, after the animation due to the setfillafter. How can I then from that location, B, reverse the animation and go back to location, A?
The Overall Question
So I'm translating a textswitcher down and then up, but of course, setfillafter only translates the bitmap of the view and not the view itself, so this may not be the best approach. I've also tried to do an animation and then offset of the view, but that looks glitchy. So basically, I want a view(textswitcher) which a swipe/fling down will move it down and touching(onClick) it when it's down will move it back up (same animation reversed). How can I do this?
I know how to do the swipe/fling and onClick stuff, just not how to implement the correct animation.
Perhaps you may want to consider adding an on touch listener to see where you touched it last, If above move up with a constant animation. This is if im understanding the question correctly.
I am trying to animate cards across the screen.
My code looks like this:
private void animate(ImageView animationSource, ImageView animationTarget, int animationDuration, int animationDelay) {
int[] animationSourcePosition = {0,0};
int[] animationTargetPosition = {0,0};
animationSource.getLocationInWindow(animationSourcePosition);
animationTarget.getLocationInWindow(animationTargetPosition);
TranslateAnimation cardDealingAnimation = new TranslateAnimation(
Animation.ABSOLUTE,animationSourcePosition[0],
Animation.ABSOLUTE,animationTargetPosition[0],
Animation.ABSOLUTE,animationSourcePosition[1],
Animation.ABSOLUTE,animationTargetPosition[1]);
cardDealingAnimation.setDuration(animationDuration);
cardDealingAnimation.setStartOffset(animationDelay);
animationTarget.startAnimation(cardDealingAnimation);
}
What happens is that it just pops up in the new place without showing the places in-between.
Funnily enough, on one of my targets it does sometimes show up.
There are two problems I see here.
One problem is that you're getting the position of your objects in the Window, where you should just use the position of the objects in their parent container. It's easier, faster, and more what you're looking for - where the objects live in their layout. If they do not live in the same container, then you may need to get the position in the window or the screen, and then just deal with delta values (the difference in their locations, not the absolute locations themselves).
You can get these simple container positions from the getLeft() and getTop() methods on View.
The second problem is that the TranslateAnimation parameters don't do what you think they do. The numeric values (the second, fourth, sixth, and eighth values) specify the starting and ending locations of the target object as offsets from its current location. That's why they have the word 'delta' in them in the method declaration. So you should not send in the absolute coordinates of the object positions their, but, rather, the deltas that they should have.
Let's suppose you have calculated an xDelta and yDelta as the difference in x and y between the source and target positions. If you're animating the source to the target position, then you would use something like 0, xDelta, 0, yDelta for these values.
Note that TranslateAnimation() does not physically move the object - it just draws it in a different place. So as soon as the animation is complete, the object will snap back to its original location. You may want to set the fillAfter property to maintain its end location on the screen when the animation completes.
I'd like to create an indeterminate animation that simply fades from one color to another (a pulse, if you will). I don't think this should require the use of images but despite my best efforts, I'm not sure I understand how to use something like AlphaAnimation with a Shape to accomplish this.
Could someone please provide some insight as to how to accomplish this? I have a feeling I'm missing something pretty straightforward here. (Examples are always appreciated!)
Thanks!
This is a trivial task in 3.0 - you can set up an ObjectAnimator to change the "color" or "backgroundColor" of an object (View, ColorDrawable, whatever has the property) between two values. See the ApiDemo animations/BouncingBalls for an example of this.
But assuming you're using pre-3.0 APIs, there are a couple of approaches. First, you could set up your own handler to give you the timing events you need, then calculate the new color at each point.
It's probably slightly easier (if not entirely intuitive) to use an AlphaAnimation. All you really want from the animation is percentage values, not to fade anything. So you don't set the animation on a view, but just set it up to run internally from a value of 0 to 1, then get the current animated value in your onDraw() method and set the current color appropriately.
For example, this will set up and start the alpha animation to run for one second:
Transformation transform = new Transformation();
AlphaAnimation anim = new AlphaAnimation(0f, 1f);
anim.setDuration(1000);
anim.start();
Then in your drawing loop, you grab the current animated value:
long time = getDrawingTime();
anim.getTransformation(time, transform);
float elapsedFraction = transform.getAlpha();
Once you have the elapsedFraction (a value between 0 and 1), you can calculate the appropriate in-between color value.
The code above may not match your situation exactly, but you should be able to do something similar to get what you want.