Animation through points array - android

I'm sure there is an easy way to do this but I'm stuck.
Let's say I have a list of points :
Point[] list = {pointA, pointB, pointC, ...}
I'd like to animate an ImageView through each point
So I tried this :
id = 0;
AnimatorListenerAdapter animEnd = new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
id++;
if(id != list.length) {
iv.animate()
.translationX(list[id].getX())
.translationY(list[id].getY())
.setDuration(200)
.setListener(this);
}
}
};
iv.animate()
.translationX(list[id].getX()).translationY(list[id].getY())
.setDuration(200).setListener(animEnd);
It works but there 's a slight delay between each animation.
Any idea? Thanks !

You probably get the delays between your animation steps because you always start a fresh animation on each transition from step to step. To overcome this situation you have multiple options.
Keyframes
Here you can find a technique called Keyframe Animation, which is a very common animation technique and is probably exactly what you want.
A Keyframe object consists of a time/value pair that lets you define a specific state at a specific time of an animation. Each keyframe can also have its own interpolator to control the behavior of the animation in the interval between the previous keyframe's time and the time of this keyframe.
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);
In your case you could map the points in your list to a list of Keyframe instances and ...
Point[] list = {pointA, pointB, pointC, ...}
List<Keyframe> kfs = new ArrayList<Keyframe>();
foreach (Point p : points) {
Keyframe kf = new Keyframe.ofFloat(p.x); // or what ever
kfs.add(kf);
}
... later pass these keyframes to some factory method to create some PropertyValuesHolder, like ofFloat that has the following signature:
public static PropertyValuesHolder ofFloat (
Property<?, Float> property,
float... values
)
The second parameter is a variable argument list which also accepts arrays as inputs. Thus it should be possible to pass your kfs to the method as a second argument, somehow.
In your case i would craft the following methods, since you are developing for dalvik VM you cannot use java8 lambda expressions:
// maps points to X/Y float values
List<Float> toArrayX(Point[] points) { ... }
List<Float> toArrayY(Point[] points) { ... }
// maps float values to Keyframes
List<Keyframe> toKeyframes(List<Float> floats) { ... }
void createAnimation(Point[] points) {
List<Keyframe> xs = toKeyframes(toArrayX(points));
PropertyValuesHolder phvX = PropertyValuesHolder
.ofKeyframe("translationX", xs);
List<Keyframe> ys = toKeyframes(toArrayY(points));
PropertyValuesHolder phvY = PropertyValuesHolder
.ofKeyframe("translationY", ys);
linkPropertyValuesHolder(phvX);
linkPropertyValuesHolder(phvY);
}
void linkPropertyValuesHolder(PropertyValuesHolder phv) {
// setup target
ObjectAnimator anim = ObjectAnimator
.ofPropertyValuesHolder(target, phv)
anim.setDuration(5000ms);
}
Interpolators
Alternatively you can specify the transitions given by the points through an Interpolator instance.
An interpolator defines the rate of change of an animation. This allows the basic animation effects (alpha, scale, translate, rotate) to be accelerated, decelerated, repeated, etc.
An interpolator maps a fractional float between 0.0 and 1.0 to another fractional float between 0.0 and 1.0. Like the following three; LinearInterpolator, AccelerateDecelerateInterpolator and BounceInterpolator:
Images from here
Path Interpolator
With PathInterpolators it is possible to draw arbritary interpolators using Path instances. The drawn path will be used to drive the animation. In your case you could create two paths, one for the x and another one for the y translation.
However, take care when constructing interpolators from paths, because
... the Path must conform to a function y = f(x).
The Path must not have gaps in the x direction and must not loop back on itself such that there can be two points sharing the same x coordinate. It is alright to have a disjoint line in the vertical direction:
So take a look at the following code snippet that is taken from here, creates a valid path and could be used as input for the PathInterpolator constructor.
Path path = new Path();
path.lineTo(0.25f, 0.25f);
path.moveTo(0.25f, 0.5f);
path.lineTo(1f, 1f);
Custom Interpolator
When the given interpolators are not flexible enough, you can also just implement the Interpolator interface and create a new instance from it afterwards. The interface is very narrow and it only provides a single method called getInterpolation with the following signature.
public abstract float getInterpolation (float input)
Code vs. XML
Another last option is to move all the animation configuration into XML instead of baking these details directly into the binary distribution of the code. However, each of the given options will require a different setup to be manageable through XML, if possible.
Hope this helps, but it is only pseudo code. I didn't test the code... so no warranties for correctness.

Related

How to make a custom interpolator for Android animation (Kotlin)?

Loosely, this is my code, boiled down to the relevant parts:
fun animate(tv: TextView) {
var animationSlide = TranslateAnimation(0.0f, 0.0f, 100.0f, 0f)
var animationScale = ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f)
animationSlide.interpolator = AccelerateInterpolator() // want custom interpolator
animationScale.interpolator = DecelerateInterpolator() // want custom interpolator
animationSlide.duration = 1000
animationScale.duration = 1000
var animationSet = AnimationSet(false)
animationSet.addAnimation(animationSlide)
animationSet.addAnimation(animationScale)
tv.startAnimation(animationSet)
}
Is it possible to make a custom interpolator which takes an array of points, or a lambda function which takes a time input and returns a value for that time point? The Android documentation only shows a small list of interpolators, none of which can be customised to a general function curve.
Thanks for any help!
This is done by making a class that implements Interpolator. Or you can use a SAM-converted lambda. For example, an interpolation based on the smoothstep function:
animationSlide.setInterpolator { amount ->
amount * amount * (3f - 2f * amount)
}
If you want to define a set of points to define your curve, you can do that with the Path class and instantiate a PathInterpolator with that. From looking at the source code, it looks like PathInterpolator approximates the Path by breaking it down into straight line segments, so if there are arcs and Beziers, it could potentially become kind of a heavy class instance that you might want to cache a reference to if you use it repeatedly.

In Android how to use ObjectAnimator to move to point x along a curve

I have an image view "stone" and am moving it from its current position to a X,Y position. I want it to move along a curve. Please let me know how I can do that(i have set the min api as 11)
ObjectAnimator moveX = ObjectAnimator.ofFloat(stone, "x", catPos[0] );
ObjectAnimator moveY = ObjectAnimator.ofFloat(stone, "y", catPos[1] );
AnimatorSet as = new AnimatorSet();
as.playTogether(moveX, moveY);
as.start();
The answer by Budius seems perfectly useful to me.
Here are the animator objects I use:
Purpose: Move View "view" along Path "path"
Android v21+:
// Animates view changing x, y along path co-ordinates
ValueAnimator pathAnimator = ObjectAnimator.ofFloat(view, "x", "y", path)
Android v11+:
// Animates a float value from 0 to 1
ValueAnimator pathAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
// This listener onAnimationUpdate will be called during every step in the animation
// Gets called every millisecond in my observation
pathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
float[] point = new float[2];
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Gets the animated float fraction
float val = animation.getAnimatedFraction();
// Gets the point at the fractional path length
PathMeasure pathMeasure = new PathMeasure(path, true);
pathMeasure.getPosTan(pathMeasure.getLength() * val, point, null);
// Sets view location to the above point
view.setX(point[0]);
view.setY(point[1]);
}
});
Similar to: Android, move bitmap along a path?
you have two options:
both needs a Path object that defines your curve:
Path path = new Path();
path. // define your curve here
if using Lollipop only (API 21) use ObjectAnimator.ofFloat(...path) like this:
ObjectAnimator.ofFloat(stone, View.X, View.Y, path).start();
if that's not an options, you use an AnimatorListener to receive updates about the each animator frame and use the PathMeasure to get the values on that point, like this:
PathMeasure pm;
float point[] = {0f, 0f};
private final ValueAnimator.AnimatorUpdateListener listener =
new ValueAnimator.AnimatorUpdateListener(){
#Override
public void onAnimationUpdate (ValueAnimator animation) {
float val = animation.getAnimatedFraction();
pm.getPosTan(pm.getLength() * val, point, null);
stone.setTranslationX(point[0]);
stone.setTranslationY(point[1]);
}
}
// and then to animate
pm = new PathMeasure(path, false);
ValueAnimator a = ValueAnimator.ofFloat(0.0f 1.0f);
a.setDuration(/* your duration */);
a.setInterpolator(/* your interpolator */);
a.addUpdateListener(listener);
a.start();
Play around with the interpolators. For example set 2 different Interpolators for x and y :
moveX.setInterpolator(new AccelerateDecelerateInterpolator());
moveY.setInterpolator(new DecelerateInterpolator());
Theres more (LinearInterpolator, AccelerateInterpolator...) but I think this should be the combination you want.
Thanks for the answers, but I found my own answer.Just added some extra x,y positions in the Objectanimator stmt. It went to those positions before coming to the final one and traced a path!
ObjectAnimator moveX = ObjectAnimator.ofFloat(stone, "x", catPos[0]-20,catPos[0]-10,catPos[0] );
ObjectAnimator moveY = ObjectAnimator.ofFloat(stone, "y",catPos[1]-20,catPos[1]-10, catPos[1] );
The Path class has methods for creating non-straight lines of several types; arcs, circles, ovals, rectangles, cubic & quadratic bezier curves, which you can then animate your object along.
As Budius points out, you do need to code for API 21 (Lollipop) or later to use paths with object translation. It is now over two years old.
On the other hand, there does seem a dearth of anything-more-than-very-simple examples of using paths "in the wild", so perhaps there's a reason they've not caught on yet.
Check this link for 3 ways of animating several properties of a View in parallel:
https://developer.android.com/guide/topics/graphics/prop-animation#view-prop-animator
The 1st way of doing it is using AnimatorSet.
Below is the 2nd way:
public static ObjectAnimator ofPropertyValuesHolder (Object target,
PropertyValuesHolder... values)
The API doc describes this constructor for ObjectAnimator as:
"This variant should be used when animating several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows you to associate a set of animation values with a property name."
Example:
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder transX = PropertyValuesHolder.ofKeyframe("translationX", kf0, kf1, kf2);
PropertyValuesHolder transY = PropertyValuesHolder.ofKeyframe("translationY", kf0, kf1, kf2);
ObjectAnimator transAnim = ObjectAnimator.ofPropertyValuesHolder(view2animate, transX, transY);
transAnim.setDuration(5000);
transAnim.start();
The example above moves view2animate in both x and y axis at the same time.
The 3rd way is to use ViewPropertyAnimator.

android animationing views for multiple devices

I have a small game in my application, if you get something right balloons float up the screen, the balloons are views moved by 2 ObjectAnimators in an animation set, and on my main debugging device it works fine but on other devices (including tablets) it looks aweful the values are all over the place and the views violently sway from one side to the next.
here is a snippet of what im doing:
//2 square balloons floating
sb = (ImageView)findViewById(R.id.squareballoon);
sb.setVisibility(View.VISIBLE);
sb2 = (ImageView)findViewById(R.id.squareballoon2);
sb2.setVisibility(View.VISIBLE);
sp.play(inflate, 1, 1, 0, 0, 1);
//left balloon
ObjectAnimator sqbalAnim3 = ObjectAnimator.ofFloat(sb,"x",-500,500);
sqbalAnim3.setDuration(700);
sqbalAnim3.setRepeatCount(5);
sqbalAnim3.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator sqbalAnim = ObjectAnimator.ofFloat(sb,"y",2000,-1800);
sqbalAnim.setDuration(3000);
sqbalAnim.setRepeatMode(ValueAnimator.RESTART);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(sqbalAnim, sqbalAnim3);
animSetXY.start();
//right balloon
ObjectAnimator sqbalAnim4 = ObjectAnimator.ofFloat(findViewById(R.id.squareballoon2),"x",-1500,-500);
sqbalAnim4.setDuration(700);
sqbalAnim4.setRepeatCount(5);
sqbalAnim4.setRepeatMode(ValueAnimator.REVERSE);
ObjectAnimator sqbal2Anim = ObjectAnimator.ofFloat(findViewById(R.id.squareballoon2),"y",1800,-1800);
sqbal2Anim.setDuration(3000);
sqbal2Anim.setRepeatMode(ValueAnimator.RESTART);
AnimatorSet animSetXY2 = new AnimatorSet();
animSetXY2.playTogether(sqbal2Anim,sqbalAnim4);
animSetXY2.start();
//balloon animation end
animSetXY2.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
sp.play(dropSound,1,1,0,0,1);
sb.setBackgroundResource(R.drawable.burst);
pop = (AnimationDrawable) sb.getBackground();
pop.start();
sb2.setBackgroundResource(R.drawable.burst);
pop = (AnimationDrawable) sb2.getBackground();
pop.start();
}
});
return true;}
//end of square balloons
ive read i can use a fraction rather than explicit values, can anybody help me out with this or point in the right direction,
any and all suggestions welcome, many thanks
I guess your problem is in parameters you are using (hardcoding) like:
ObjectAnimator.ofFloat(sb,"x",-500,500);
ObjectAnimator.ofFloat(sb,"y",2000,-1800);
Since such values are only good for the device (screen actually) you are testing on. Other devices has other screens with other resolutions and you should consider it. Also you should maybe calc values from dp to pixels. What are 500 and 2000 actually? If its pixels then most probably thats the problem.
Take a look here (for dp to px):
What is the difference between "px", "dp", "dip" and "sp" on Android?
Android: 'dp' to 'px' conversion?

Android ValueAnimator Sin wave

I've got a Custom ViewGroup class and an image I've added to it. When the screen is tapped I want to animate the added image to travel across the screen in a wavelike pattern. What I have now is below but though the image moves in a wavelike pattern it jumps around too quickly and is a blur. How can I slow it down to move in a steady wave?
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); // values from 0 to 1
if(animateImage) {
incrementalValue = 0f;
animator.setDuration(4000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float) (animation.getAnimatedValue()))
.floatValue();
float amplitude = 100f;
mImage.setTranslationX(incrementalValue);
mImage.setTranslationY((float) (amplitude * Math.sin((incrementalValue) * Math.PI)));
incrementalValue += 5f;
}
});
animator.setTarget(mImage);
animator.start();
}
You declare a float value in onAnimationUpdate and never use it again. The calcualtion for the image translation is mImage.setTranslationY((float) (amplitude * Math.sin((incrementalValue) * Math.PI)));. If you insert the values and incrementalValue ( = 0) doesn't change, this calculation will always return 0 because sin(0) = 0 and one 0 in a multiplication makes the product 0. You should insert animator.getAnimatedValue for incrementalValue. Besides, i suggest you change the calculation to sin(animator.getAnimatedValue()*PI*2) this makes the wavelength of the sine 1 and the image will move up and down once per repetition of the animation and it won't jump when the aimation restarts.
Note you can still multiply with amplitude to make the image move a certain distance.

How to prepare curve translate animation for android?

There are 4 types of animations in android - rotate, alpha,scale and translate.
I want to prepare curved translate animation.
Is it possible.?
What Android version do you use? Since API level 11 you can use custom Animators which can easily implement your curve translation.
If you use a version below that there is afaik only the possibility to manually concatenate multiple linear translations using the translate animation and setting animation listeners
EDIT:
Example:
View view;
animator = ValueAnimator.ofFloat(0, 1); // values from 0 to 1
animator.setDuration(5000); // 5 seconds duration from 0 to 1
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = ((Float) (animation.getAnimatedValue()))
.floatValue();
// Set translation of your view here. Position can be calculated
// out of value. This code should move the view in a half circle.
view.setTranslationX((float)(200.0 * Math.sin(value*Math.PI)));
view.setTranslationY((float)(200.0 * Math.cos(value*Math.PI)));
}
});
I hope it works. Just copied & pasted (and shortened and changed) the code from one of my apps.
Here are the animators I use:
Purpose: Move View "view" along Path "path"
Android v21+:
// Animates view changing x, y along path co-ordinates
ValueAnimator pathAnimator = ObjectAnimator.ofFloat(view, "x", "y", path)
Android v11+:
// Animates a float value from 0 to 1
ValueAnimator pathAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
// This listener onAnimationUpdate will be called during every step in the animation
// Gets called every millisecond in my observation
pathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
float[] point = new float[2];
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Gets the animated float fraction
float val = animation.getAnimatedFraction();
// Gets the point at the fractional path length
PathMeasure pathMeasure = new PathMeasure(path, true);
pathMeasure.getPosTan(pathMeasure.getLength() * val, point, null);
// Sets view location to the above point
view.setX(point[0]);
view.setY(point[1]);
}
});
Similar to: Android, move bitmap along a path?
Consider the following web link. It is a game in C. You need to isolate the projectile() function and try to understand the variables defined within it. Once you get that try implementing it in your own code.
http://www.daniweb.com/software-development/c/code/216266

Categories

Resources