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

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.

Related

Square Root formula in Calculator

Ok I am having an issue trying to put a square root equation in a calculator. I am trying to figure out the expression builder. I know the expression builder takes the math operations of add, subtract, multiply, divide, equals and the parenthesis. What I am doing is trying to build the square root section. I have a simple Percent code to help with the square root.
In the Square root vs the Percent you see I am using binding. So here is the code for both.
On the square root is it possible to use the expression builder? I know there is no absolute formula for square root except for a number that is multipliable with itself like the number 4.
Sqrt(4) = 2
binding.btnSqrt.setOnClickListener {
var square = (tv_equation.text.toString().toDouble() / 2)
binding.tvResult.text = square.toString()
}
So in the event you a non square equation
sqrt(23) = 4.79
How would I simulate that as one function within the button. Can I use expression or would I need to use Kotlin.math
So between the two I divide by 100 on the percent. It works great.
binding.btnPercent.setOnClickListener {
var percentage = (tv_equation.text.toString().toDouble() / 100)
binding.tvResult.text = percentage.toString()
}
All my other buttons work fine and so I am just working on the square root before I can release this to google play.
You would need to use some form of a square root function.
AS you mentioned in your question Kotlin Sqrt Function is a very suitable choice
binding.btnSqrt.setOnClickListener {
if(!tv_equation.text.isNullOrEmpty){
var number = tv_equation.text.toString().toDouble()
binding.tvResult.text = sqrt(number).toString()
}
You can create a sqrt function using the Quake's first inverse square root.
Quake's first inverse square root.
Pseudo Code:
float InvSqrt(float x){
float xhalf = 0.5f * x;
int i = *(int*)&x; // store floating-point bits in integer
i = 0x5f3759df - (i >> 1); // initial guess for Newton's method
x = *(float*)&i; // convert new bits into float
x = x*(1.5f - xhalf*x*x); // One round of Newton's method
return x;
}
So the answer i want to add is a little more suitable with the previous answer. Maybe this will help some people in the future. This solution will also limit the decimal space to 4 decimals.
binding.btnSqrt.setOnClickListener{
val df = DecimalFormat("#.####")
if(!tv_equation.text.isNullOrEmpty())
{
val number = tv_equation.text.toString().toDouble()
binding.tvResult.text = df.format(sqrt(number))
}
}
you can adjust the val df = DecimalFormat("#.####") where the # to as many decimals as you would want.

What is compose equivalent of OvershootInterpolator?

On Android views, we had an animation using an OvershootInterpolator
We want to replicate the same animation in Jetpack Compose.
I see that there are several AnimationSpec described in https://developer.android.com/jetpack/compose/animation but I don't see which one might replicate the OvershootInterpolator
tween spec doesn't seem to do any overshoot, just animating between start and end value without overshooting
spring spec does overshooting, however it doesn't have a durationMillis parameter as tween, so we can't control how fast it plays
keyFrames spec seems a possible solution by doing something like this:
animationSpec = keyframes {
durationMillis = 500
0f at 100 with FastOutSlowInEasing
// Overshoot value to 50f
50f * 2 at 300 with LinearOutSlowInEasing
// Actual final value after overshoot animation
25f at 500
}
Is there a better / simpler way than using keyFrames to replicate OvershootInterpolator?
You can use any Interpolator from Animator world via a custom Easing:
animationSpec = tween(easing = Easing {
OvershootInterpolator().getInterpolation(it)
})
See Easing interface: https://developer.android.com/reference/kotlin/androidx/compose/animation/core/Easing
Though I would recommend using springs over interpolators, especially ones that mimic springs. Springs would give a much more natural look. :)
We can use spring animation of compose to achieve OvershootInterpolator. Even though we cannot provide any custom duration for the animation, we can control the speed of the animation using the stiffness attribute.
We can put any custom float values to the stiffness attribute.
androidx.compose.animation.core currently provides 4 stiffness constant values by default i.e,
object Spring {
/**
* Stiffness constant for extremely stiff spring
*/
const val StiffnessHigh = 10_000f
/**
* Stiffness constant for medium stiff spring. This is the default stiffness for spring
* force.
*/
const val StiffnessMedium = 1500f
/**
* Stiffness constant for a spring with low stiffness.
*/
const val StiffnessLow = 200f
/**
* Stiffness constant for a spring with very low stiffness.
*/
const val StiffnessVeryLow = 50f
.....
}
We can check which stiffness suits our case.
For eg:- (medium speed)
animationSpec = spring(
dampingRatio = Spring.DampingRatioHighBouncy,
stiffness = Spring.StiffnessMedium // with medium speed
)
We can also put a custom value to the stiffness for eg:-
animationSpec = spring(
dampingRatio = Spring.DampingRatioHighBouncy, // this would define how far the overshoot would happen.
stiffness = 1000f // with custom speed less than medium speed.
)
You can play around with custom float values for stiffness to achieve your ideal duration for the animation.

Android alpha animation: override initial alpha value

Basically I want something to fade-in and then fade-out. Initially it should be invisible, and when the animation is ended, it should also be invisible.
targetView.alpha = 0f;
var aa = AlphaAnimation(0f, 1.0f);
aa.duration=2000;
aa.repeatMode = Animation.REVERSE;
aa.repeatCount = 1;
I have tried the code above, but it did not work. it seems that the alpha animation is multiplying the initial alpha with that animation. So, 0f * 1.0f = 0f = invisible.
After searching for an answer, I have tried this, but it did not work.
aa.fillBefore= true;
aa.fillAfter=true;
How can I make the animation ignore the initial alpha value? Is it impossible, and I should change the visibility at the start/end of the animation manually?
Yes you are right about "it seems that the alpha animation is multiplying the initial alpha with that animation. So, 0f * 1.0f = 0f = invisible.".
Just don't use targetView.alpha = 0f;, use targetView.setVisibility(View.INVISIBLE); instead.

Animation through points array

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.

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