I have a few buttons in an Android app of mine, "Start", "Stop", and "Pause". When Start is pressed is becomes disabled and Stop and Pause become enabled. Similarly, when Stop or Pause is pressed, Start becomes enabled again. When these buttons change state, I would like to animate the change with a bouncing animation to visually indicate to the user that the button is now available. The problem is that the animation is actually changing the buttons size(background). Check out this gif to see what I mean (watch the Stop button):
Here is the code for the animation:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<scale
android:fromXScale="1.0"
android:toXScale="1.1"
android:fromYScale="1.0"
android:toYScale="1.1"
android:pivotX="50%"
android:pivotY="50%"
android:duration="200"
android:repeatCount="0"
android:repeatMode="reverse" />
</set>
And how I'm applying it to the buttons:
Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale_button);
startButton.startAnimation(scaleAnimation);
stopButton.startAnimation(scaleAnimation);
Why is the Stop button size changing and how can I keep it from doing this?
Thanks!
Note: I originally set the "repeatCount" of the animation to 1 so that it would scale the button back down. I changed it to 0 to debug this problem but the same thing is still happening.
I'd refrain from using Animation. You have to play with fillEnabled, fillAfter, fillBefore flags, it's not convenient and non-intuitive API to use.
Instead, prefer Animators API.
The animation you'd like to implement will look something like this:
val scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, 1.0f, 1.1f).setDuration(200)
val scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, 1.0f, 1.1f).setDuration(200)
val downscaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, 1.1f, 1.0f).setDuration(200)
val downscaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, 1.1f, 1.0f).setDuration(200)
val set = AnimatorSet()
set.playTogether(scaleX, scaleY)
set.play(downscaleX).after(scaleX)
set.playTogether(downscaleX, downscaleY)
set.start()
Or, you can play with fluent API:
view.animate()
.scaleXBy(0.1f)
.scaleYBy(0.1f)
.setDuration(200)
.withEndAction {
view.animate()
.scaleXBy(-0.1f)
.scaleYBy(-0.1f)
.duration = 200
}
You can achieve this simply by
v.animate().scaleX(1.1f).scaleY(1.1f).setDuration(200).withEndAction(new Runnable() {
#Override
public void run() {
v.animate().scaleX(1.0f).scaleY(1.0f).setDuration(200);
}
});
where "v" is your view.
Regards
I have a Linear Layout which I want to be animated to resize when I click on it. Once it is resized you should be able to see buttons and text. I've tried animating using this:
container.animate().scaleX(scale);
container.animate().scaleY(scale);
Also I tried this:
container.startAnimation(anim);
where anim is (for making the view bigger):
<scale
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="10.0"
android:fromYScale="1.0"
android:toYScale="10.0"
android:pivotX="0%"
android:pivotY="0%"
android:fillAfter="false"
android:duration="700" />
But the problem is that each time I resize the LinearLayout the children inside get resized as well. This should be compatible to AndroidSDK 16 and above. How to achieve this without resizing the children inside?
Thanks
Hello you can override onMeasure() method of the children and make its dimensions as constants for example.
float scale = 1.2f;
ValueAnimator animator = ValueAnimator.ofInt(0,100);
animator.setTarget(container);
animator.setDuration(1000).start();
animator.addUpdateListener(new AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate(ValueAnimator animation)
{
LayoutParams layoutparams = container.getLayoutParams();
layoutparams.height = (int)(layoutparams.height* animation.getAnimatedValue() *(scale -1f));
layoutparams.width= (int)(layoutparams.width* animation.getAnimatedValue() *(scale -1f));
container.setLayoutParams(layoutparams);
}
});
I have Relative layout, in that layout I have image views which altogether looks like one image. I want animate the 6 image views. 3 are right_top,right_center,right_bottom and another 3 are left-top,left-center,left-bottom. and all these are within its parent layout only...
I want to implement exactly like this image.
Hope THIS may help you.
And if you want to add scaling to tour animation add this scale inside your each animation set
<scale
android:fromYScale="1.0"
android:toYScale="0.5"
android:startOffset="0"
android:duration="1200"
android:fromXScale="1.0"
android:toXScale="0.5"
android:fillAfter="true" />
siddhu,sorry for late.
#Override
public void onClick(View v) {
TranslateAnimation mAnimation1 = new TranslateAnimation(0, mImageViewX.getX(),
mImageView1.getY(), mImageViewX.getY());
mAnimation1.setDuration(500);
mAnimation1.setFillAfter(true);
mImageView1.startAnimation(mAnimation1);
TranslateAnimation mAnimation2 = new TranslateAnimation(0,
- mImageViewX.getX(), mImageView2.getY(), mImageViewX.getY());
mAnimation2.setDuration(500);
mAnimation2.setFillAfter(true);
mImageView2.startAnimation(mAnimation2);
TranslateAnimation mAnimation3 = new TranslateAnimation(0, mImageViewX.getX(),
mImageView3.getX(), -mImageViewX.getY());
mAnimation3.setDuration(500);
mAnimation3.setFillAfter(true);
mImageView3.startAnimation(mAnimation3);
}
The xml is this :
you should learn animation first..it's not hard.
android.view.animation.TranslateAnimation.TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
```
How can I create one animation which always translates from left to right and then if animation stops it turns the other way and translates from right to left. I can create Translate animation in xml and programatically too.
Create this xml, it will translate from left to right and from right to left.
/res/anim/translate.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillEnabled="true"
android:fillAfter="true" >
<translate
android:interpolator="#android:anim/linear_interpolator"
android:fromXDelta="0%p"
android:toXDelta="10%p"
android:duration="2000"
android:startOffset="0" />
<translate
android:interpolator="#android:anim/linear_interpolator"
android:fromXDelta="10%p"
android:toXDelta="-10%p"
android:duration="2000"
android:startOffset="2000" />
</set>
Suppose you want to apply this animation to an image then write the code below in your class file.
ImageView image = (ImageView)findViewById(R.id.imageView1);
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.translate);
image.startAnimation(animation);
Edit:
If you want your animation to repeat infinitely add the following attributes to the translate tag.
android:repeatCount="infinite"
android:repeatMode="restart"
Depends on your min API level actually. Essentially You should look at ViewPropertyAnimator class.
Let's say you have view to animate, and parent - parent of this view. Your code would look like this:
final float startX = 0; //start position
final float endX = parent.getWidth() - view.getWidth(); //end position - right edge of the parent
API 12+:
view.animate().translationX(endX).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.animate().translationX(startX).start();
}
}).start();
API 16+:
view.animate().translationX(endX).withEndAction(new Runnable() {
#Override
public void run() {
view.animate().translationX(startX).start();
}
}).start();
Your startX and endX might be different - depending on your needs.
Please note that start() method call is optional.
Also I have not mentioned solution for API <12 since I personally think that nobody should support those legacy APIs :)
I'm using a RotateAnimation to rotate an image that I'm using as a custom cyclical spinner in Android. Here's my rotate_indefinitely.xml file, which I placed in res/anim/:
<?xml version="1.0" encoding="UTF-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:duration="1200" />
When I apply this to my ImageView using AndroidUtils.loadAnimation(), it works great!
spinner.startAnimation(
AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely) );
The one problem is that the image rotation seems to pause at the top of every cycle.
In other words, the image rotates 360 degrees, pauses briefly, then rotates 360 degrees again, etc.
I suspect that the problem is that the animation is using a default interpolator like android:iterpolator="#android:anim/accelerate_interpolator" (AccelerateInterpolator), but I don't know how to tell it not to interpolate the animation.
How can I turn off interpolation (if that is indeed the problem) to make my animation cycle smoothly?
You are right about AccelerateInterpolator; you should use LinearInterpolator instead.
You can use the built-in android.R.anim.linear_interpolator from your animation XML file with android:interpolator="#android:anim/linear_interpolator".
Or you can create your own XML interpolation file in your project, e.g. name it res/anim/linear_interpolator.xml:
<?xml version="1.0" encoding="utf-8"?>
<linearInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
And add to your animation XML:
android:interpolator="#anim/linear_interpolator"
Special Note: If your rotate animation is inside a set, setting the interpolator does not seem to work. Making the rotate the top element fixes it. (this will save your time.)
I had this problem as well, and tried to set the linear interpolator in xml without success. The solution that worked for me was to create the animation as a RotateAnimation in code.
RotateAnimation rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(5000);
rotate.setInterpolator(new LinearInterpolator());
ImageView image= (ImageView) findViewById(R.id.imageView);
image.startAnimation(rotate);
This works fine
<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1600"
android:fromDegrees="0"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="358" />
To reverse rotate:
<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1600"
android:fromDegrees="358"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="0" />
Maybe, something like this will help:
Runnable runnable = new Runnable() {
#Override
public void run() {
imageView.animate().rotationBy(360).withEndAction(this).setDuration(3000).setInterpolator(new LinearInterpolator()).start();
}
};
imageView.animate().rotationBy(360).withEndAction(runnable).setDuration(3000).setInterpolator(new LinearInterpolator()).start();
By the way, you can rotate by more than 360 like:
imageView.animate().rotationBy(10000)...
Try using toDegrees="359" since 360° and 0° are the same.
ObjectAnimator.ofFloat(view, View.ROTATION, 0f, 360f).setDuration(300).start();
Try this.
Rotation Object programmatically.
// clockwise rotation :
public void rotate_Clockwise(View view) {
ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 180f, 0f);
// rotate.setRepeatCount(10);
rotate.setDuration(500);
rotate.start();
}
// AntiClockwise rotation :
public void rotate_AntiClockwise(View view) {
ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 180f);
// rotate.setRepeatCount(10);
rotate.setDuration(500);
rotate.start();
}
view is object of your ImageView or other widgets.
rotate.setRepeatCount(10); use to repeat your rotation.
500 is your animation time duration.
Pruning the <set>-Element that wrapped the <rotate>-Element solves the problem!
Thanks to Shalafi!
So your Rotation_ccw.xml should loook like this:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="-360"
android:pivotX="50%"
android:pivotY="50%"
android:duration="2000"
android:fillAfter="false"
android:startOffset="0"
android:repeatCount="infinite"
android:interpolator="#android:anim/linear_interpolator"
/>
In Kotlin:
ivBall.setOnClickListener(View.OnClickListener {
//Animate using XML
// val rotateAnimation = AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely)
//OR using Code
val rotateAnimation = RotateAnimation(
0f, 359f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
)
rotateAnimation.duration = 300
rotateAnimation.repeatCount = 2
//Either way you can add Listener like this
rotateAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {
}
override fun onAnimationRepeat(animation: Animation?) {
}
override fun onAnimationEnd(animation: Animation?) {
val rand = Random()
val ballHit = rand.nextInt(50) + 1
Toast.makeText(context, "ballHit : " + ballHit, Toast.LENGTH_SHORT).show()
}
})
ivBall.startAnimation(rotateAnimation)
})
As hanry has mentioned above putting liner iterpolator is fine. But if rotation is inside a set you must put android:shareInterpolator="false" to make it smooth.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
**android:shareInterpolator="false"**
>
<rotate
android:interpolator="#android:anim/linear_interpolator"
android:duration="300"
android:fillAfter="true"
android:repeatCount="10"
android:repeatMode="restart"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator"
android:duration="3000"
android:fillAfter="true"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0"
android:toYScale="0" />
</set>
If Sharedinterpolator being not false, the above code gives glitches.
If you are using a set Animation like me you should add the interpolation inside the set tag:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<rotate
android:duration="5000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:startOffset="0"
android:toDegrees="360" />
<alpha
android:duration="200"
android:fromAlpha="0.7"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toAlpha="1.0" />
</set>
That Worked for me.
No matter what I tried, I couldn't get this to work right using code (and setRotation) for smooth rotation animation. What I ended up doing was making the degree changes so small, that the small pauses are unnoticeable. If you don't need to do too many rotations, the time to execute this loop is negligible. The effect is a smooth rotation:
float lastDegree = 0.0f;
float increment = 4.0f;
long moveDuration = 10;
for(int a = 0; a < 150; a++)
{
rAnim = new RotateAnimation(lastDegree, (increment * (float)a), Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rAnim.setDuration(moveDuration);
rAnim.setStartOffset(moveDuration * a);
lastDegree = (increment * (float)a);
((AnimationSet) animation).addAnimation(rAnim);
}
Try to use more than 360 to avoid restarting.
I use 3600 insted of 360 and this works fine for me:
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="3600"
android:interpolator="#android:anim/linear_interpolator"
android:repeatCount="infinite"
android:duration="8000"
android:pivotX="50%"
android:pivotY="50%" />
Here is code snippet that worked fine for me:
RotateAnimation rotate = new RotateAnimation(
0, 359,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
rotate.setDuration(1500);
rotate.setRepeatCount(Animation.INFINITE);
yourView.startAnimation(rotate);
Check it's not 360 it's 359 in the constructor as 0 and 360 are at the same point.
Is it possible that because you go from 0 to 360, you spend a little bit more time at 0/360 than you are expecting? Perhaps set toDegrees to 359 or 358.
private fun rotateTheView(view: View?, startAngle: Float, endAngle: Float) {
val rotate = ObjectAnimator.ofFloat(view, "rotation", startAngle, endAngle)
//rotate.setRepeatCount(10);
rotate.duration = 400
rotate.start()
}
The 100% correct answer
var rotate = RotateAnimation(
0F, 360F,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
)
rotate.duration = 999
rotate.repeatCount = Animation.INFINITE
imageview..setAnimation(rotate)
100% working!!!
In Android, if you want to animate an object and make it move an object from location1 to location2, the animation API figures out the intermediate locations (tweening) and then queues onto the main thread the appropriate move operations at the appropriate times using a timer. This works fine except that the main thread is usually used for many other things — painting, opening files, responding to user inputs etc. A queued timer can often be delayed. Well written programs will always try to do as many operations as possible in background (non main) threads however you can’t always avoid using the main thread. Operations that require you to operate on a UI object always have to be done on the main thread. Also, many APIs will funnel operations back to the main thread as a form of thread-safety.
Views are all drawn on the same GUI thread which is also used for all user interaction.
So if you need to update GUI rapidly or if the rendering takes too much time and affects user experience then use SurfaceView.
Example of rotation image:
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private DrawThread drawThread;
public MySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
drawThread = new DrawThread(getHolder(), getResources());
drawThread.setRunning(true);
drawThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
class DrawThread extends Thread{
private boolean runFlag = false;
private SurfaceHolder surfaceHolder;
private Bitmap picture;
private Matrix matrix;
private long prevTime;
public DrawThread(SurfaceHolder surfaceHolder, Resources resources){
this.surfaceHolder = surfaceHolder;
picture = BitmapFactory.decodeResource(resources, R.drawable.icon);
matrix = new Matrix();
matrix.postScale(3.0f, 3.0f);
matrix.postTranslate(100.0f, 100.0f);
prevTime = System.currentTimeMillis();
}
public void setRunning(boolean run) {
runFlag = run;
}
#Override
public void run() {
Canvas canvas;
while (runFlag) {
long now = System.currentTimeMillis();
long elapsedTime = now - prevTime;
if (elapsedTime > 30){
prevTime = now;
matrix.preRotate(2.0f, picture.getWidth() / 2, picture.getHeight() / 2);
}
canvas = null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(picture, matrix, null);
}
}
finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
activity:
public class SurfaceViewActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MySurfaceView(this));
}
}
you can use this code:
view.animate().rotation(360.0f).setDuration(1000);