I wanna make animation like domino effect.
but when i use animation xml and handler with for loop, it works together at the same time.
how can i make the domino effect animation such as start the animation for next button(tue) 100 milliseconds after the first button(mon) started?
my hanlder method here
private void startAnimation(final ArrayList<View> vArr)
{
mon = (Button)findViewById(R.id.mon);
tue = (Button)findViewById(R.id.tue);
wed = (Button)findViewById(R.id.wed);
thu = (Button)findViewById(R.id.thu);
fri = (Button)findViewById(R.id.fri);
sat = (Button)findViewById(R.id.sat);
sun = (Button)findViewById(R.id.sun);
final Button[] weekDayList = {mon, tue, wed, thu, fri, sat, sun};
Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch (msg.what)
{
case 3:
for (int i = 0; i < weekDayList.length; i++)
weekDayList[i].startAnimation(buttonDown);
sendEmptyMessageDelayed(4, 100);
break;
case 4:
break;
}
}
};
handler.sendEmptyMessage(3);
}
my animation xml here
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:toXScale="1.0"
android:fromYScale="1.0"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="100%"
android:duration="500" />
</set>
You can use ObjectAnimator (since API 11) to do this and have the animations play sequentially by adding them all to an AnimatorSet.
public void dominoAnimations() {
ObjectAnimator monAnimation = ObjectAnimator.ofFloat(mon, "scaleY", 0f);
ObjectAnimator tueAnimation = ObjectAnimator.ofFloat(tue, "scaleY", 0f);
ObjectAnimator wedAnimation = ObjectAnimator.ofFloat(wed, "scaleY", 0f);
ObjectAnimator thuAnimation = ObjectAnimator.ofFloat(thu, "scaleY", 0f);
ObjectAnimator friAnimation = ObjectAnimator.ofFloat(fri, "scaleY", 0f);
ObjectAnimator satAnimation = ObjectAnimator.ofFloat(sat, "scaleY", 0f);
ObjectAnimator sunAnimation = ObjectAnimator.ofFloat(sun, "scaleY", 0f);
AnimatorSet dominoes = new AnimatorSet();
dominoes.playSequentially(
monAnimation, tueAnimation, wedAnimation,
thuAnimation, friAnimation, satAnimation, sunAnimation
);
dominoes.setDuration(500);
dominoes.start();
}
Set AnimationListener to the animations. Use onAnimationEnd() to start the next animation.
Related
I'm trying to do a shake animation. It works well when I do it in XML, like this:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="50"
android:fromDegrees="-0.02"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="30"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:toDegrees="0.02" />
<translate
android:fromXDelta="-0.02"
android:toXDelta="0.02"
android:repeatCount="30"
android:repeatMode="reverse"
android:interpolator="#android:anim/linear_interpolator"
android:duration="50" />
</set>
But I want to do it using ObjectAnimator, so I'll be able to use AnimatorSet to play this animation at the same time of other animations I've done.
I tried to do it like that:
val pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, -0.05f, 0.05f)
val pvhT = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, -0.05f, 0.05f)
val rotate = ObjectAnimator.ofPropertyValuesHolder(imageView, pvhR). apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
val translate = ObjectAnimator.ofPropertyValuesHolder(imageView, pvhT).apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
But the translate animation makes my imageView disappear. I had problems with rotation animations before (when I tried to do rotation animation, the imageView changed its position, but I fixed it by using .ofPropertyValuesHolder, instead of using .ofFloat). Probably because I'm using a custom library that implements a zoomable/pinchable layout.
Now I'm trying to do this translate animation, but it doesn't works even with .ofPropertyValuesHolderz. It's working only with XML, but as I said, I can't put it inside an AnimatorSet().
Try this code
val set = AnimatorSet()
val rotation = ObjectAnimator.ofFloat(imageViewMain, "rotation", -0.05f, 0.05f).apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
val translation = ObjectAnimator.ofFloat(imageViewMain, "translationX", -0.05f, 0.05f).apply {
duration = 50
repeatCount = 20
repeatMode = ValueAnimator.REVERSE
}
set.playTogether(rotation, translation)
I would like to create a ObjectAnimator programmatically that represents this xml -
<objectAnimator
android:duration="#integer/eye_anim_duration"
android:interpolator="#android:interpolator/anticipate_overshoot"
android:propertyName="pathData"
android:repeatCount="0"
android:repeatMode="restart"
android:valueFrom="#string/big"
android:valueTo="#string/small"
android:valueType="pathType"
/>
In similar fashion as I can create a ObjectAnimator for color change -
ValueAnimator animator = ObjectAnimator.ofInt(vector, "fillColor", getResources().getColor(R.color.light_green), getResources().getColor(R.color.red));
animator.setDuration(3000);
Using PathAnimatorInflater class and rewriting the setupAnimatorForPath to accept Strings instead TypedArray it is possible. Here is a method that uses this -
private ObjectAnimator getAnimator(AnimatedVectorDrawable vector, int in, int out, int delay) {
ObjectAnimator animator = new ObjectAnimator();
animator.setTarget(vector);
animator.setPropertyName("pathData");
TypeEvaluator evaluator = PathAnimatorInflater.setupAnimatorForPath(animator, getResources().getString(in), getResources().getString(out));
if (evaluator == null) {
return null;
}
animator.setEvaluator(evaluator);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setDuration(3000);
animator.setStartDelay(delay);
return animator;
}
But to use this with AnimatedVectorDrawable it needs some changes in Vector classes also.
I have an image of a bullet in an ImageView that does Translate animation.
I need to show real time coordinates to show how far it is from target in real time.
ImageView myimage = (ImageView)findViewById(R.id.myimage);
Animation animation = new TranslateAnimation(100, 200, 300, 400);
animation.setDuration(1000);
myimage.startAnimation(animation);
animation.setRepeatCount(Animation.INFINITE);
Is it possible to get real time x and y coordinates of the image while it is doing TranslateAnimation ?
And if its not possible using TranslateAnimation, is there any other way that gives real time coordinates of image while in motion ?
I tried -
int x = myimage.getLeft();
int y = myimage.getTop();
and
int[] firstPosition = new int[2];
myimage.measure(View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
myimage.getLocationOnScreen(firstPosition);
int x = firstPosition[0];
int y = firstPosition[1];
but in both the ways, its giving the initial static coordinate of the ImageView.
Here's a complete example based on what user3249477 and Vikram said:
final TextView positionTextView = (TextView)findViewById(R.id.positionTextView);
ImageView myimage = (ImageView)findViewById(R.id.imageView);
ObjectAnimator translateXAnimation= ObjectAnimator.ofFloat(myimage, "translationX", 0f, 100f);
ObjectAnimator translateYAnimation= ObjectAnimator.ofFloat(myimage, "translationY", 0f, 100f);
translateXAnimation.setRepeatCount(ValueAnimator.INFINITE);
translateYAnimation.setRepeatCount(ValueAnimator.INFINITE);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(translateXAnimation, translateYAnimation);
set.start();
translateXAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
imageXPosition = (Float)animation.getAnimatedValue();
}
});
translateYAnimation.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
imageYPosition = (Float)animation.getAnimatedValue();
String position = String.format("X:%d Y:%d", (int)imageXPosition, (int)imageYPosition);
positionTextView.setText(position);
}
});
You can use an ObjectAnimator (API 11+):
ImageView iv = (ImageView)findViewById(R.id.markerRed);
// Create animators for x and y axes
ObjectAnimator oax = ObjectAnimator.ofFloat(iv, "translationX", 0f, 100f);
ObjectAnimator oay = ObjectAnimator.ofFloat(iv, "translationY", 0f, 100f);
oax.setRepeatCount(Animation.INFINITE);
oay.setRepeatCount(Animation.INFINITE);
// Combine Animators and start them together
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(oax, oay);
set.start();
Then fetch the animation values like this:
Log.e("TAG", "X: " + oax.getAnimatedValue() + " Y:" + oay.getAnimatedValue());
If you add these values to the initial ImageView's coordinates, you'll get the current location.
I had this idea: you can extend the standard TranslateAnimation and intercept every step of the animation by overriding the applyTransformation method.
Take a look at this incomplete/untested snippet:
private class ObservableTranslateAnimation extends TranslateAnimation{
private float matrixValues[] = new float[9];
private float actualDx;
private float actualDy;
public ObservableTranslateAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
}
// ... more constructors
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
//After that super is called, the matrix gets updated!
//get the current matrix and extract what you need
t.getMatrix().getValues(matrixValues);
actualDx = matrixValues[Matrix.MTRANS_X];
actualDy = matrixValues[Matrix.MTRANS_Y];
/*
Notify someone here (a listener?), or just read the values of actualDx, actualDy from outside
You can also play around with the other Matrix values.
*/
}
}
I'm doing some animation in my application. Works fine except for one small detail. The UI is unresponsive until the animation is completed. I cannot scroll, not do anything else.
I read that putting this in a Runnable is not the solution. So I'm at a lose.
Ultimately I'd like to have each object using a different duration based on the size of the object so the animation runs faster on a smaller circle and slower on a bigger circle.
Here is the code I have to test my animation :
HoleView holeView = (HoleView) view.findViewById(R.id.holeView1);
ObjectAnimator oa1 = ObjectAnimator.ofInt(holeView, "animationTime", 0, holeView.getAnimationTime());
oa1.setDuration(holeView.getAnimationTime());
holeView = (HoleView) view.findViewById(R.id.holeView2);
ObjectAnimator oa2 = ObjectAnimator.ofInt(holeView, "animationTime", 0, holeView.getAnimationTime());
oa2.setDuration(holeView.getAnimationTime());
holeView = (HoleView) view.findViewById(R.id.holeView3);
ObjectAnimator oa3 = ObjectAnimator.ofInt(holeView, "animationTime", 0, holeView.getAnimationTime());
oa3.setDuration(holeView.getAnimationTime());
holeView = (HoleView) view.findViewById(R.id.holeView4);
ObjectAnimator oa4 = ObjectAnimator.ofInt(holeView, "animationTime", 0, holeView.getAnimationTime());
oa4.setDuration(holeView.getAnimationTime());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(oa1).with(oa2).with(oa3).with(oa4);
animatorSet.start();
Try using value animator instead:
Create a method that returns a ValueAnimator:
public static ValueAnimator animate(float from, float to, long duration) {
ValueAnimator anim = ValueAnimator.ofFloat(from, to);
anim.setDuration(duration);
return anim;
}
Then where you use it do the following if for instance you want to set the scale of an ImageView called imageView
//where 0 is the start value, 1 is the end value, and 850 is the duration in milliseconds
ValueAnimator imageAnimator = animate(0, 1, 850);
imageAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator anim) {
float scale = (Float) anim.getAnimatedValue();
imageView.setScaleX(scale);
imageView.setScaleY(scale);
}
});
imageAnimator.start();
So Id like to rotate a handful of views all at the same time, all using the same rotation specs. The issue is that for some reason the rotation acts differently for the second element. Apparently this has to do with the animation object actually changing state in between those two lines of code. Obviously I could just create a seperate Animation object and apply it, but I feel like there is an easier way (I have about 15 views)
Rotates only the first view correctly:
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotationtoportrait);
target.startAnimation(rotateAnim);
lightBtn.startAnimation(rotateAnim);
Rotates both correctly
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotationtoportrait);
Animation rotateAnim2 = AnimationUtils.loadAnimation(this, R.anim.rotationtoportrait);
target.startAnimation(rotateAnim);
lightBtn.startAnimation(rotateAnim2);
XML:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="-90"
android:toDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="500" android:fillAfter="true">
Anyone have any ideas?
Do it like this:
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "y", 100f);
arrayListObjectAnimators.add(anim);
ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "x", 0f);
arrayListObjectAnimators.add(anim1);
ObjectAnimator[] objectAnimators = arrayListObjectAnimators.toArray(new ObjectAnimator[arrayListObjectAnimators.size()]);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(objectAnimators);
animSetXY.duration(1000);
animSetXY.start();
So I guess this just isn't possible, so I created a helper method to just apply the same animation to a list of views:
public void doRotations(ArrayList<View> views, int start, int end, int xprop, float xscale, int yprop, float yscale, int duration, Boolean fillAfter){
for(int i = 0; i < views.size(); i++){
RotateAnimation temp = new RotateAnimation(start, end, xprop, xscale, yprop, yscale);
temp.setDuration(duration);
temp.setFillAfter(fillAfter);
views.get(i).startAnimation(temp);
}
}
Definitely a hack, but I guess thats all I'm able to do right now
I was able to do this in Kotlin by programmatically creating one AnimatorSet.
1. Create an ArrayList of the views you want to animate
var viewList = arrayListOf(view1,view2,view3)
2. Loop through the ArrayList and create a growing AnimatorSet
var ix = 0
var anim = AnimatorSet()
var viewList = arrayListOf(view1,view2,view3)
viewList.forEach{
// Initiate the animator set with one ObjectAnimator
if(ix == 0){
anim = AnimatorSet().apply {
play(ObjectAnimator.ofFloat(it, "rotation", 0F, 360F))
}
}
// Add one ObjectAnimator at a time to the growing AnimatorSet
else{
var currentAnim = ObjectAnimator.ofFloat(it,"rotation",0F,360F)
anim = AnimatorSet().apply {
play(anim).with(currentAnim)
}
}
ix++
}
3. Start the animation
button.setOnClickListener {
AnimatorSet().apply {
play(anim)
start()
}