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
Related
I wish to have an animated background like this gif:
.
I don't have a mac or adobe after effects software to create a lottie json for such animation, and i want a more command over animation than simply using a gif. From what i have read over the internet, I have to use either TransitionManager or TranslationAnimation , but i am unable to figure out the correct code for it.
I am also interested in knowing if there is a plugin, or some way to make minor edits to already available lottie files like background color change , items color change, items removal, etc just like the way we could do for simple vector images(Using Android Studio, or at the very least, some free software). I found this lottie file which was doing a very great job for me, but i couldn't modify its colors
found it! When i logged the values of some variables like view.getX() or view.getRootView().getX(), i was able to figure out, that my view will have its location as 0,0 . so After that it was all a need for finding the screen size to finally make this infinite cloud moving animation. I am not sure if this function is good in terms of memory or performance , but on my device, its running smoothly.
private void showCloudMovingContinuouslyAnimation(View v) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int runtimeScreenWidth = displayMetrics.widthPixels;
int totalDuration = runtimeScreenWidth*5;
int viewPositionX = 0, viewPositionY = 0;
TranslateAnimation outToRight = new TranslateAnimation(
viewPositionX, (runtimeScreenWidth / 2f) + 50,
viewPositionY, viewPositionY);
outToRight.setDuration(totalDuration / 2);
outToRight.setRepeatCount(Animation.INFINITE);
TranslateAnimation inFromLeft = new TranslateAnimation(
-((runtimeScreenWidth / 2f) + 50), viewPositionX,
viewPositionY, viewPositionY
);
inFromLeft.setDuration(totalDuration / 2);
inFromLeft.setRepeatCount(Animation.INFINITE);
boolean shouldRemainThereAfterAnimationFinishes = true;// useful when animating for single time
AnimationSet animationSet=new AnimationSet(true);
animationSet.setInterpolator(new LinearInterpolator());
animationSet.setRepeatCount(Animation.INFINITE);
animationSet.addAnimation(inFromLeft);
animationSet.addAnimation(outToRight);
v.startAnimation(animationSet); // start animation
}
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.
After few hours of trying I'm looking for some hints on how to add snap-scroll mechanism to MPAndroid. Basically I want the 5 visible bars to align so they are fully visible and centered. I now imported the library source code because it looks like there's no other way to change the code in computeScroll (BarLineChartTouchListener).
Edit:
To clarify - I'm showing around 20 bars but chart is zoomed so user can scroll horizontally. What bothers me it is not getting aligned automatically so first visible bar might be clipped in half. I'm looking for snapping effect where it will round the position to the nearest multiplication of the bar width, leaving 5 fully visible bars.
I ended up adding the following function in BarLineChartBase.java. I know it's far from elegant, but seems to do the job. It's limited to targetApi > 11, because of the ValueAnimator. For lower API (which I don't cater for) you might need to have a look at nineoldandroids or some other animation loop technique.
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void alignX() {
int count = this.getValueCount();
int xIndex = this.getLowestVisibleXIndex() + Math.round( (this.getHighestVisibleXIndex() - this.getLowestVisibleXIndex()) / 2.0f );
float xsInView = this.getXAxis().getValues().size() / this.getViewPortHandler().getScaleX();
Transformer mTrans = this.getTransformer(YAxis.AxisDependency.LEFT);
float[] pts = new float[] { xIndex - xsInView / 2f, 0 };
mTrans.pointValuesToPixel(pts);
final Matrix save = new Matrix();
save.set(this.getViewPortHandler().getMatrixTouch());
final float x = pts[0] - this.getViewPortHandler().offsetLeft();
final int frames = 20;
ValueAnimator valueAnimator = new ValueAnimator().ofInt(0, frames);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
int prev = -1;
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if( (int) animation.getAnimatedValue() > prev ) {
save.postTranslate( -x / (float)frames, 0);
BarLineChartBase.this.getViewPortHandler().refresh(save, BarLineChartBase.this, true);
}
prev = (int) animation.getAnimatedValue();
}
});
valueAnimator.start();
}
I trigger it at the end of computeScroll function in BarLineChartTouchListener.
I kept names of variables as I copied code from functions like MoveViewJob, ViewPortHandler etc. Since it's only aligning in x axis - I removed Y axis calculations and used zeros instead. Any optimizations welcome, especially from the author #PhilippJahoda.
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.
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.