99

I created an anim.xml file such as below to shake imageview like IOS icon shaking in android. However it does not provide me same result. Is there any better idea?

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"

    android:fromDegrees="-2"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:toDegrees="2" />
Winston
  • 1,800
  • 2
  • 20
  • 30

15 Answers15

183

Try setting android:repeatMode="reverse". Below animation gives a very reasonable immitation on my Galaxy Nexus. Obviously you can fine tune the parameters to your own liking.

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="100"
    android:fromDegrees="-5"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toDegrees="5" />
MH.
  • 45,303
  • 10
  • 103
  • 116
  • i am new to android development, i am using Xamarin , can you please tell me how can i apply this animation to a image/button on any event like click etc. – N.K Apr 13 '16 at 08:54
  • @N.K: It should work the same as in Java: load the animation by its resource ID and tell your view to start the animation by passing it as parameter. Should look something like: `view.StartAnimation(AnimationUtils.LoadAnimation(Resource.Animation.wobble))`. More details [here](https://developer.xamarin.com/guides/android/application_fundamentals/graphics_and_animation/#View_Animations) and [here](http://stackoverflow.com/a/30841945/1029225). – MH. Apr 13 '16 at 13:19
  • 3
    How can we add the delay between shaking. for example show shake for 500 ms then delay for 1000 ms then repeat again? – Damir Mailybayev Mar 22 '17 at 07:24
105

Nice shake animation;

res/anim/shake.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate android:duration="150"
        android:fromXDelta="-10%"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:toXDelta="10%"/>
</set>

How to use it

final Animation animShake = AnimationUtils.loadAnimation(this, R.anim.shake);
btn_done = (Button) findViewById(R.id.btn_act_confirm_done); 
btn_done.startAnimation(animShake);

How to use it (Simpler version):

btn_done.startAnimation(AnimationUtils.loadAnimation(this,R.anim.shake));
Community
  • 1
  • 1
Samir
  • 6,405
  • 5
  • 39
  • 42
48

You could try this:

shake.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android" 
           android:fromXDelta="0" 
           android:toXDelta="10" 
           android:duration="1000" 
           android:interpolator="@anim/cycle_7" />

cycle_7.xml

<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android" 
                   android:cycles="7" />
robd
  • 9,646
  • 5
  • 40
  • 59
adneal
  • 30,484
  • 10
  • 122
  • 151
  • 12
    hey i guess `cycle_7.xml` can be substituted to `android:repeatCount="7"` attribute in `` – Anup Sep 24 '15 at 20:07
28

try to use this one:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="70"
        android:fromDegrees="-5"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:interpolator="@android:anim/linear_interpolator"
        android:toDegrees="5" />
    <translate
        android:fromXDelta="-10"
        android:toXDelta="10"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="70" />
</set>
Simon
  • 13,173
  • 14
  • 66
  • 90
21

To make shake effect like this

enter image description here

First define shake animation inside anim folder as shake.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="70"
        android:fromDegrees="-5"
        android:interpolator="@android:anim/linear_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:toDegrees="5" />
    <translate
        android:duration="70"
        android:fromXDelta="-10"
        android:interpolator="@android:anim/linear_interpolator"
        android:repeatCount="5"
        android:repeatMode="reverse"
        android:toXDelta="10" />
</set>

Then in code

if (TextUtils.isEmpty(phone.getText())
 || phone.getText().length() < 10)
    {
     //shake animation
    phone.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.shake));
     }
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
16

I created a shake effect on Android and posted in GitHub. See if it works better.

https://github.com/teoinke/ShakeAnimation

Relevant code:

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/overshoot_interpolator"
    android:fillAfter="true">

    <translate
        android:startOffset="100"
        android:fromXDelta="0%p"
        android:toXDelta="10%p"
        android:duration="50" />

    <translate
        android:startOffset="150"
        android:fromXDelta="0%p"
        android:toXDelta="-25%p"
        android:duration="110" />


    <translate
        android:startOffset="260"
        android:fromXDelta="0%p"
        android:toXDelta="25%p"
        android:duration="120" />


    <translate
        android:startOffset="380"
        android:fromXDelta="0%p"
        android:toXDelta="-20%p"
        android:duration="130" />


    <translate
        android:startOffset="510"
        android:fromXDelta="0%p"
        android:toXDelta="10%p"
        android:duration="140" />

</set>
Teo Inke
  • 5,928
  • 4
  • 38
  • 37
  • 1
    For me this is the best answer, as it animates from the center and doesn't instantly offset the image like some of the other suggestions. Pro! – Jonathan Dunn Aug 16 '18 at 10:50
14

This one works pretty well (though not perfectly) as an iOS "incorrect PIN" shaking clone:

    final float FREQ = 3f;
    final float DECAY = 2f;
    // interpolator that goes 1 -> -1 -> 1 -> -1 in a sine wave pattern.
    TimeInterpolator decayingSineWave = new TimeInterpolator() {
                @Override
                public float getInterpolation(float input) {
                    double raw = Math.sin(FREQ * input * 2 * Math.PI);
                    return (float)(raw * Math.exp(-input * DECAY));
                }
            };

    shakeField.animate()
            .xBy(-100)
            .setInterpolator(decayingSineWave)
            .setDuration(500)
            .start();
lincolnq
  • 1,305
  • 10
  • 7
12
/**
 *
 * @param view      view that will be animated
 * @param duration  for how long in ms will it shake
 * @param offset    start offset of the animation
 * @return          returns the same view with animation properties
 */
public static View makeMeShake(View view, int duration, int offset) {
    Animation anim = new TranslateAnimation(-offset,offset,0,0);
    anim.setDuration(duration);
    anim.setRepeatMode(Animation.REVERSE);
    anim.setRepeatCount(5);
    view.startAnimation(anim);
    return view;
}

use:

TextView tv;
makeMeShake(tv,20,5);    // it will shake quite fast
Windless
  • 121
  • 1
  • 4
11

For Kotlin users:

First create an Animation resource file called shake.xml. Right click on the res folder in Android Studio, then click New > Android Resource File > enter shake for the file name and select Animation for Resource type dropdown. Click OK.

Inside shake.xml paste the following:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:duration="200"
               android:fromXDelta="-5%"
               android:repeatCount="3"
               android:repeatMode="reverse"
               android:toXDelta="5%"/>
</set>

Now just call it on a view!

From within a fragment:

myView.startAnimation(AnimationUtils.loadAnimation(requireContext(), R.anim.shake))

From within an activity:

myView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.shake))

(note - myView is the ID given to the view that you want to animate)

If you would like to fine-tune the animation, simply modify the values in shake.xml.

Carmen
  • 6,177
  • 1
  • 35
  • 40
Joe
  • 3,772
  • 3
  • 33
  • 64
3

I created a very good approximation of iOS shaking (when you long press a icon to remove app from homescreen). You have to apply inside your code, programmatically, as it requires random number generation:

int dur1 = 70 + (int)(Math.random() * 30);
int dur2 = 70 + (int)(Math.random() * 30);

// Create an animation instance
Animation an = new RotateAnimation(-3, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

// Set the animation's parameters
an.setDuration(dur1);               // duration in ms
an.setRepeatCount(-1);                // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE);
an.setFillAfter(true);               // keep rotation after animation


// Create an animation instance
Animation an2 = new TranslateAnimation(-TranslateAnimation.RELATIVE_TO_SELF,0.02f,
        TranslateAnimation.RELATIVE_TO_SELF,0.02f,
        -TranslateAnimation.RELATIVE_TO_SELF,0.02f,
        TranslateAnimation.RELATIVE_TO_SELF,0.02f);

// Set the animation's parameters
an2.setDuration(dur2);               // duration in ms
an2.setRepeatCount(-1);                // -1 = infinite repeated
an2.setRepeatMode(Animation.REVERSE);
an2.setFillAfter(true);               // keep rotation after animation

AnimationSet s = new AnimationSet(false);//false means don't share interpolators
s.addAnimation(an);
s.addAnimation(an2);

// Apply animation to image view
itemView.setAnimation(s);

This code was design to be applied inside an adapter's gridview (getView), but you can apply to any view by changing the last line to:

yourViewName.setAnimations(s);

1

Kotlin version of lincolnq's answer

val FREQ = 3f
val DECAY = 2f
            
val decayingSineWave = TimeInterpolator { input ->
   val raw = sin(FREQ * input * 2 * Math.PI)
   (raw * exp((-input * DECAY).toDouble())).toFloat()
}

// where binding.loginFrame is the view you wanna shake
binding.loguinFrame.animate()
 .withEndAction{
    // here you can clear the fields after the shake
 }
 .xBy(-100f)
 .setInterpolator(decayingSineWave)
 .setDuration(500)
 .start()
TadeoArmenta
  • 154
  • 6
  • 16
1

If you want to shake your view then slowly stop, we can reduce the translation value after each round.
This is a helper function

fun animateHorizontalShake(
    view: View,
    offset: Float,
    repeatCount: Int = 3,
    dampingRatio: Float? = null,
    duration: Long = 1000L,
    interpolator: Interpolator = AccelerateDecelerateInterpolator()
) {
    val defaultDampingRatio = dampingRatio ?: (1f / (repeatCount + 1))
    val animValues = mutableListOf<Float>()
    repeat(repeatCount) { index ->
        animValues.add(0f)
        animValues.add(-offset * (1 - defaultDampingRatio * index))
        animValues.add(0f)
        animValues.add(offset * (1 - defaultDampingRatio * index))
    }
    animValues.add(0f)

    val anim: ValueAnimator = ValueAnimator.ofFloat(*animValues.toFloatArray())
    anim.addUpdateListener {
        view.translationX = it.animatedValue as Float
    }
    anim.interpolator = interpolator
    anim.duration = duration
    anim.start()
}

Example slow

animateHorizontalShake(imageView, 200f, repeatCount = 4, duration = 10000L)

enter image description here

Example fast

animateHorizontalShake(imageView, 50f, repeatCount = 4, duration = 1000L)

enter image description here

Or even if you dont want the shake show down

animateHorizontalShake(imageView, 50f, dampingRatio = 0f, interpolator = LinearInterpolator())

With above code, also remember to use dp value for offset for support multiple devices instead of hardcode

Linh
  • 57,942
  • 23
  • 262
  • 279
0

IOS wobble animation is not that simple try to change pivot x and y randomly when rotate. You should change the value programatically though. May be you also can use translate animation simultaneously

0

Banging my head for more than two hours, I knew how to shake and wobble an view.

Unfortunately the accepted answer won't work apart from onCreateView of fragment.

Example if you have onClick method and inside in it. You have animation like below it won't work.

Please go through the code.

    DoneStart.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View view) {
           register(view);

        }
    });

The register method has some checks like below code

 private void register(View view) {
    String type = typedThings.getText.toString(); 
   String  km = Km_Now.getText().toString();

    if (serviceType == null) {
        animationServiceList = AnimationUtils.loadAnimation(getActivity(), R.anim.shake_wobble);
        silverServiceButton.setAnimation(animationServiceList);
        generalServiceButton.setAnimation(animationServiceList);
        platinumServiceButton.setAnimation(animationServiceList);
        animationServiceList.start();
    } else if (km == null) {
        animationEditText = AnimationUtils.loadAnimation(getActivity(), R.anim.shake_wobble);
        Km_Now.setAnimation(animationEditText);
        animationEditText.start();
    }

The Call animationServiceList.start(); will never be called,

SOLUTION: Use PropertyAnimator like ObjectAnimator.

EngineSense
  • 3,266
  • 8
  • 28
  • 44
0

Other answers are correct as well but this is a bit smoother than them since it uses an interpolator produces smooth numbers for back an forth movement

    public class WobblyView extends ImageView implements ValueAnimator.AnimatorUpdateListener {
    private final ValueAnimator va = ValueAnimator.ofInt(-10, 10);

    public WobblyView(Context context) {
        this(context, null);
    }

    public WobblyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WobblyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setAdjustViewBounds(true);
        setImageResource(R.drawable.ic_logo);
        va.setInterpolator(new AccelerateDecelerateInterpolator());
        va.setRepeatMode(ValueAnimator.REVERSE);
        va.setRepeatCount(ValueAnimator.INFINITE);
        va.setDuration(1000);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        va.addUpdateListener(this);
        va.start();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        va.removeUpdateListener(this);
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int heading = (int) animation.getAnimatedValue();
        setRotation(heading);
    }
}
Deliganli
  • 221
  • 2
  • 10