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.
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);
}
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
With PathInterpolator
s 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);
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With