机器人中的震动/摆动视图动画

我创建了一个 anim.xml 文件(如下所示)来像在 android 中抖动 IOS 图标一样抖动图像视图。 然而,它并没有给我同样的结果。 还有更好的办法吗?

<?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" />
88499 次浏览

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" />

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" />

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

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>

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>

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.

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));

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));
}

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);
}
}

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);

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();
/**
*
* @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

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.

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()