Is there a way to parametrise an animation? - android

I've created a simple animation to make a view move up and down repeatedly:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:fromYDelta="0"
android:toYDelta="???"
android:duration="1000"
android:repeatCount="1000000"
android:repeatMode="reverse"/>
</set>
I'm using an animation resource for that like it is suggested in the docs. However, at compile time I don't know how far down the view is supposed to go (toYDelta) as it will depend on the device screen.
Is there a way to parametrise that somehow and if not, what would be a way around it?

You can implement the same animation programmatically like that in Kotlin:
val bounceAnimation = TranslateAnimation(0f, 0f, fromYDelta, toYDelta).apply {
duration = 1000
repeatCount = 1000000
repeatMode = Animation.REVERSE
interpolator = AccelerateDecelerateInterpolator()
}
view.startAnimation(bounceAnimation)

You can express those Values as Percentage, eg. : "20%p" is 20% of the parent
See for more info
https://developer.android.com/guide/topics/resources/animation-resource

Documentation says here:
A vertical and/or horizontal motion. Supports the following attributes in any of the following three formats: values from -100 to 100 ending with "%", indicating a percentage relative to itself; values from -100 to 100 ending in "%p", indicating a percentage relative to its parent; a float value with no suffix, indicating an absolute value. Represents a TranslateAnimation.
%p will help you do what you want.

Related

Android how to slide view from left to right

I have an ImageView I would like to make it slide with an animation from its original position to the new one.
Currently my view is on the left close to the screen border. I need to move it to the opposite side and have it stayed there permanently.
Is there a way to accomplish this ?
For now I've tried Transition Animation like this one :
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate android:fromXDelta="0%" android:toXDelta="500%"
android:fromYDelta="0%" android:toYDelta="0%" android:fillAfter="true"/>
</set>
But the problem is that the androud:toXDelta value is arbitrary and I don't know how to calculate it.
With the current value, the view seems to come from outside the screen and goes a little bit further than its original position but at the end of the animation immediately comes back to its original position. That is not what I am looking for.
Thanks for the help !
To get the ImageView to stay at its final location at the end of the animation, put the following attributes inside your <set> tag:
android:fillAfter="true"
android:fillEnabled="true"
You can also do this type of thing pretty easily programmatically using a TranslateAnimation. You would do something similar to this:
ImageView viewToSlide = (ImageView) findViewById(R.id.your_image_view);
TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0,
Animation.RELATIVE_TO_PARENT, 0.80f,
Animation.RELATIVE_TO_PARENT, 0,
Animation.RELATIVE_TO_PARENT, 0);
animation.setDuration(5000);
animation.setFillEnabled(true);
animation.setFillAfter(true);
viewToSlide.startAnimation(animation);
EDIT IN RESPONSE TO COMMENTS
To answer your first question below:
So you had originally mentioned that xDelta was an "arbitrary" value that was difficult to calculate, and you are right! So the way we can position our view relative to the width of its parent is to use the Animation.RELATIVE_TO_PARENT identifier, which identifies our next dimension value as a value that should be multiplied by the dimension of the parent itself. So in the constructor of our TranslateAnimation, we have -- Animation.RELATIVE_TO_PARENT, 0.80f -- which just means "take the width of the parent, and multiply it by 0.80." That gives us 80% of the width of the parent. The other values with zeroes in them just give us values of zero, because it will be "the width/height of the parent multiplied by zero."
To answer your second question:
The reason the animation accelerates is because I believe the default interpolator (the animation modifier that defines the speeds at which the values of the animation play out) is one of the accelerator interpolators. You can change this using the setInterpolator() method, and pass in any of the values found here: http://developer.android.com/reference/android/R.interpolator.html

Android translate animation move to absolute position

is there a way to tell a translate animation to always move to a absolute position, instead to a position relative to the card. As far as I know, using android:toXDelta only moves it to a relative position. I want it to move to a absolute position (lets say width of the screen / 2 and height of the screen / 2) from every point on the screen.
My animation:
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="-20%p"
android:toYDelta="-20%p" />
Judging from official Documentation it is not possible from XML.
However, you can do this in your Java code. Example:
view.animate().translationX(0f).translationY(0f).setInterpolator(new AccelerateDecelerateInterpolator()).start();
TranslationX and TranslationY animates to an absolute position. There is also TranslationXBy an TranslationYBy that animate relatively.

How to use scale animation to hide image in Android

Is it possible to hide image using scale animation in xml. What I mean is that I have an image on the screen and I want to hide it using scale animation in the direction from 0.0 -> 1.0. So the image is on the screen and I want to start scaling it from left to right until it disappear. Is it possible?
This is not what I want. You see my app is suporting devices below 3.0. And I don't want to move an image. I want to scale it, but in opposite direction. The scale animation works with the coords of the screen, so the image will be shown if you start from 0.0 to 1.0. But I want to hide it from 0.0 to 1.0. Is it possible?
First scaling means changing the size of the image which is not what you want! you want to change the position of an image. With property animation (supported in android 3.0+) you can animate any property of an object (even non visual properties).
use this code to animate any property
ValueAnimator move = ValueAnimator.ofFloat(0,100); //start and ending value
scaleUp.setDuration(300);
scaleUp.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
Float newValue = (Float) valueAnimator.getAnimatedValue();
myObject.setX(newValue); //in pixcels
}
});
move.start();
This code changes the value of "x" property from 0 to 100 over the period of 300ms. x and y are position of your view in pixels. of course you need to first calculate start and ending point in pixels.
findout more about "x" property here http://developer.android.com/reference/android/view/View.html#setX(float)
you can also animate setTranslationX istead http://developer.android.com/reference/android/view/View.html#setTranslationX(float)
As i said earlier with property animation you can animate any property you want and it's really easy to work with, to learn more about it read this http://developer.android.com/guide/topics/graphics/prop-animation.html
I managed to do what I wanted. I used scale animation with the tag pivotX. With pivotX or pivotY you can set from what point of the image to start the animation and in what direction. You have to play a little bit with it until you recive your desired animation. Here is my code:
<scale
android:duration="4000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="450"
android:interpolator="#android:anim/linear_interpolator"
android:startOffset="300"
android:toXScale="0.0"
android:toYScale="1.0" />

Translation of image from center to top in android

Hi I have stuck with the problem of creating a splash screen where the image is placed in center_vertical|center horizontal.how to translater over to center_horizontal|top in android.can any one please guide me how to do it.
Use animations to do this, for more information about animating views, you could refer to this link and also this
What you are actually looking for in translating a view from the center to the top of the screen, you could use translation animation using the xml.
1.Create a folder anim in the res folder
2.Add a resource file that describes your translation effect like :
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="50%p" android:fromYDelta="50%p"
android:toXDelta="50%p" android:toYDelta="0%p"
android:duration="1000"
android:fillAfter="true" />
You could experiment with the % values.
3.implement this in code like :
translateAnim= AnimationUtils.loadAnimation(getApplicationContext(),
R.anim.translate_anim);
imageView.startAnimation(translateAnim);
Hope this much of clue suffices!
P.S: You could experiment with fillAfter true or false, so that you could understand their effects better.
This is I think best and easiest approach to achieve animation from center to top:
ivSplashCenter.animate()
.translationY(-((rlContainer.height - (ivSplashCenter.height * 2)) / 2).toFloat())
.setInterpolator(AccelerateInterpolator()).duration = 1500
Here, ivSplashCenter is imageview and rlContainer is my root view of XML
NOTE:
One thing you need to understand here is that I am keeping a space from the top edge of the height of the image by ivSplashCenter.height * 2,
If you don't want any padding/space at the top then you can just use ivSplashCenter.height

How to define a screen-size specific animation in XML

I created an Animation for the Fragments in my application. The Animation animates moving between tabs, for that purpose I need to animate the current Fragment to move completely off screen while the need Fragment slides in from the opposite side. Kind of like the Animation the ViewPager uses.
I need to specify an absolute starting and ending position of the View and since different devices have different dimensions I cannot define an Animation in XML that fits all devices. On a bigger device the View might not slide of screen completely and on a smaller device the View moves to much and continues moving when he is already off screen.
So I guess my question is: How can I define an animation in XML which will slide a Fragment off screen and at the same time fit on all devices?
My animation:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="x"
android:valueType="floatType"
android:valueTo="720"
android:valueFrom="0"
android:duration="300"/>
</set>
1) Regarding the bounty:
Existing answers on SO suggest subclassing the FrameLayout...
If you want to use an ObjectAnimator you have no choice but to subclass the View you want to animate. You need to provide the ObjectAnimator with the necessary getter and setter methods to do its magic as it essentially just calls those getter and setter methods to perform the Animation.
The question you are linking to (Animate the transition between fragments) is subclassing FrameLayout to add a setXFraction() and a getXFraction() method. They are implemented in a way to set the x value relative to the width of the FrameLayout. Only by doing this can the ObjectAnimator do anything else besides animating between absolute values.
So to summarise, the ObjectAnimator itself doesn't actually do much animating, it just calls getter and setter methods through reflection.
Is there really no way to get the actual screen pixel dimensions (not
just dp) into the xml file?
With an ObjectAnimator there is no way to achieve that. ObjectAnimators just interpolate from a start value to and end value. As I explained above, the setter method defines what actually happens.
For example, calling a custom function that returns the width would be
fine, or defining a constant that code can set, having code set said
constant to equal the screen width, then accessing that constant from
the xml would be equally useful.
You cannot insert any value from code into any xml resource. Everything contained in your xml files is compiled into your APK when you build it and cannot be changed at runtime. And that is also an answer to your other question: There is no resource or constant or anything which would be accessible in xml which contains the current screen size. Dynamic values like the screen size of the device the app is installed on cannot be a part of those resources since everything in them is compiled into your app when you build it.
2) Possible Solution: View Animations
One possible solution is to use view animations instead of an ObjectAnimator. With view animations you can specify fractions instead of just absolute values:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="-100%" android:toYDelta="0%" android:duration="1000"/>
</set>
This would animate a View from -100% to 0% of the screen height. In other words from completely of the screen to the position of the View. You can use it like this:
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_down);
view.startAnimation(animation);
I realise that this might not be of any help to you. There are cases in which one has to use ObjectAnimators and cannot use view animations. But as long as you are able to use view animations this should pretty much solve your problem.
Link to
documentation
Related question
3) Best practice
What we really should be addressing here is what I think is a misconception on your part. As you already noticed every animation you define in xml has absolute start and end values. Those values are static and cannot be changed after compiling your app.
If you look at how the resources work with the many available selectors and also with dp, sp... then you will realise that it is designed to do one thing:
You can for example define an animation that moves a View by 100dp. Dp is a measurement of physical size, 100dp will be the exact same physical size one any screen with any pixel density. Through selectors you could alter this animation for devices with smaller or bigger screens where the animation may be moving the View too much or too little. But you can only fill in static values. Aside from working with selectors you cannot customise the animation for each device.
So you see, the resources are really just designed for anything that's static and unchanging. It works great for dimensions or string translations but sometimes with animations it can be a bit of a pain. As I said above only view animations provide a way around the static nature by providing the option of specifying fractions instead of absolute values. But in general you cannot define anything dynamic in xml.
To futher corroborate this argument look at Google's excellent DevBytes videos about animation:
View Animations
Custom Activity Animations
Cardflip Animation
Property Animations
ListView Animations
Then you will notice that not a single animation in those examples is ever defined in xml. Sure one could argue that they don't define them in xml because they want to have an easier time explaining and showing the code, but in my opinion this once again proves one point:
You cannot define an animation in static resources which depends on a purely dynamic value
Since the screen width/height will be different from device to device you need different animations for each device. Only view animations provide a way around that since they allow you to define fractions instead of absolute values. In any other case you are going to need to define the animations programmatically. Fortunately that's not difficult with ObjectAnimators:
Animator animator = ObjectAnimator.ofFloat(view, View.X, startValue, endValue);
animator.start();
This would animate the x position of the View from the start to the end value in pixels. You can pass in the width of the View to animate it like you want to:
Animator animator = ObjectAnimator.ofFloat(view, View.X, -view.getWidth(), 0.0f);
animator.start();
This would animate the View to slide in from the left. It starts completely off screen and stops in its final position. You could also just do this:
view.animate().x(targetValue);
This would animate the View from its current x position to the target x value.
But please don't misunderstand me, you should still try to define as much as possible - be it animations or anything else - in the resources. Hardcoding animations or any other value should be avoided as much as possible, unless it's necessary like in your case.
4) Summary
So to summarise:
You cannot do what you want to do with ObjectAnimators without adding the getter and setter methods you need to the View you want to animate.
If possible use view animations. With them you can define animations based on fractions of the width or height of the View.
If the above doesn't work or is not applicable to your situation then define the animations programmatically, this will work in any case.
I hope I could help you and if you have any further questions feel free to ask!
just create different folders based on screen densities.
so lets say your xml for animation is in a folder called anim (in res parent folder).
then create anim-ldpi , anim-mdpi , etc in res as well. Put your respective animation in each of these folders which represent screen density and android will select the right one.
Somebody was asking for my working solution. Here it is. It is C# code via Mono. Hopefully Java authors can figure out what it should be in that language.
public class MyFragment: Fragment {
public int TransitDuration {
get {
return 500;
}
}
public override Animator OnCreateAnimator(FragmentTransit transit, bool enter, int nextAnim) {
switch (transit) {
case FragmentTransit.FragmentOpen:
{
if (enter) {
return this.EnterFromLeftAnimator;
} else {
return this.ExitToRightAnimator;
}
}
case FragmentTransit.FragmentClose:
{
if (enter) {
return this.EnterFromRightAnimator;
} else {
return this.ExitToLeftAnimator;
}
}
default:
Animator r = base.OnCreateAnimator(transit, enter, nextAnim);
if (r == null) {
if (!this.IsAdded) {
this.OnContextHidden();
}
}
return r;
}
}
public Animator EnterFromLeftAnimator {
get {
float width = Screen.MainScreen.PixelSize.Width; // this is an object of mine; other code cached the width there long ago. It would actually be better to use the window width.
ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", width, 0);
animator.SetDuration(this.TransitDuration);
Animator r = animator as Animator;
return r;
}
}
public Animator ExitToRightAnimator {
get {
float width = Screen.MainScreen.PixelSize.Width;
ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", 0, -width);
animator.SetDuration(this.TransitDuration);
Animator r = animator as Animator;
r.AddListener(new AnimatorEndListenerAdapter(() => this.OnContextHidden()));
return r;
}
}
public Animator EnterFromRightAnimator {
get {
float width = this.ScreenWidth;
ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", -width, 0);
animator.SetDuration(this.TransitDuration);
Animator r = animator as Animator;
return r;
}
}
public Animator ExitToLeftAnimator {
get {
float width = this.ScreenWidth;
ObjectAnimator animator = ObjectAnimator.OfFloat(this, "X", 0, width);
animator.SetDuration(this.TransitDuration);
Animator r = animator as Animator;
r.AddListener(new AnimatorEndListenerAdapter(() => this.OnContextHidden()));
return r;
}
}
public override void OnActivityCreated(Bundle savedInstanceState) {
base.OnActivityCreated(savedInstanceState);
this.RetainInstance = true;
}
}

Categories

Resources