I'm animating a view and I want to reset the view to the original position after animation ended.
This is what I have:
rl2 is a relativeLayout
rl2.animate().translationX(-60).translationY(117).setDuration(2000);
I tried setting this but its not working:
rl2.clearAnimation();
clearAnimation(); does not reset your animations, it just stops them and removes them from the animation queue. To undo your animations you need to actually undo them. So, for your code block you will need to call rl2.animate().translationX(0).translationY(0).setDuration(2000); to move the view back to its original position.
As #Chris Stillwell mentioned on his answer, But you can move View back to it's original position after translation animation by
rl2.animate().translationX(0).translationY(0);
I know this question was asked long time ago but someone may still look for answer.
I use the Animation class for this kind of thing; there is a setFillAfter method in this class, so you can set if you want the animation to apply its transformation after it ends.
Animation anim = new ScaleAnimation(
1f, 1f, // Start and end values for the X axis scaling
1f, 2f, // Start and end values for the Y axis scaling
Animation.RELATIVE_TO_SELF, 0f, // Pivot point of X scaling
Animation.RELATIVE_TO_SELF, 1f); // Pivot point of Y scaling
anim.setFillAfter(false); // no needed to keep the result of the animation
anim.setDuration(1000);
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
gangsterImage.startAnimation(anim);
If you're going to remove view don't use animation.removeAllListeners(), the function cannot continue that's why you will see some error. If you want to disappear a view, use View.GONE
in Kotlin like this:
animation.doOnEnd {
binding.mainLayout.visibility = View.GONE}
But by this way you couldn't remove any animation! So it' better rest them to the initial position.
binding.tvM2.animate().translationX(0f).translationY(0f).rotation(0f)
just use anim.setFillAfter(false); to reset automatically after animation ends.
Related
I'm trying to implement a circular reveal animation which consist to reveal the view according the user finger when he's touching the view.
First, my view is inside an infinite circular reveal loop to keep the rounded aspect as long as the user do nothing.
Then when the user touch the view and start to swipe I calculated a factor inside the ACTION_MOVE to increase the radius in real time and call the circular reveal at each pass.
public void reveal(final View view,
final float centerX,
final float centerY,
final float startRadius,
final float endRadius,
final int duration) {
anim = ViewAnimationUtils.createCircularReveal(view, (int)centerX, (int)centerY, (int)startRadius, (int)endRadius);
anim.setDuration(duration);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
if (isRepeating) {
reveal(view,centerX,centerY,startRadius,endRadius,duration);
}
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}
I recall the circular reveal method again inside its Listener to create the loop and the most important to avoid visual distortion.
I have noticed that if I didn't do this way some very fast distortion showed up because the animation duration is faster than each pass in ACTION_MOVE so the anim have time to finish before the next pass.
The distortion is the representation of the missing part of the view when the animation is finished and came back very fast to the current radius set with the Touch like a flash.
So this way with the method recall inside the Listener is working great until there.
Problem : On one of my devices there are senseless distortion in all directions while swiping.
According to my logs it's not a radius distortion because the start and end radius is always the same after each pass so the radius should not be moving like this especially when the finger is stopped, and the animation never stop thanks to the Listener like I explained above.
What am I doing wrong ?
I searched a lot and didn't find the same problem so I tried other ways like drawing canvas but unfortunately it doesn't match with my needs.
Is there another way to achieve this kind of animation even completely differently ?
Any lead is appreciate thanks in advance
EDIT : inside the ACTION_MOVE
scaleRadius = (float) (2 * distance / maxWidth + startScale);
if (scaleRadius < 1) {
scaleRadius = 1;
}
animator.reveal(this, middleCircleX, middleCircleY,
initialCircleRadius*scaleRadius, initialCircleRadius*scaleRadius, 0);
Logs : Always the same factor then the radius should not move because my finger is stopped
2020-03-27 15:42:56.113 onTouch: scaleRadius 1.7870371
2020-03-27 15:42:56.113 onTouch: scaleRadius 1.7870373
2020-03-27 15:42:56.227 onTouch: scaleRadius 1.7870371
2020-03-27 15:42:56.227 onTouch: scaleRadius 1.7870373
2020-03-27 15:42:56.331 onTouch: scaleRadius 1.7870374
2020-03-27 15:42:56.331 onTouch: scaleRadius 1.7870371
I am assuming that you dont want the flickering animation but rather that the radius of your circe transitions smoothly and becomes either bigger or smaller as a function of how far you swipe on the screen. (please correct me if this assumption is wrong)
I will assume that centerX and centerY are always the same value and are basically global variables.
Create a couple of additional global variables:
boolean animIsCurrentlyPlaying=false;
float mCurrentRadius=initialCircleSize; //i guess this is 1 in your case
float mTargetRadius;
Now on the ACTION_MOVE event you will do the following:
1- set mTargetRadius to be some function of the distance between centerX and centerY and the position of the finger on the screen (euclidean distance might be a good pick here is an example);
mTargetRadius=Float.valueOf(Math.sqrt((double)(Math.pow(event.getX()-centerX,2)+Math.pow(event.getY()-centerY,2))));
2- excecute the following method revealModified(view, 100);
public void revealModified(final View view,
final int duration) {
if(!animIsCurrentlyPlaying){
//here i just reference the global vars
anim = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, mCurrentRadius, mTargetRadius);
mCurrentRadius=mTargetRadius;
anim.setDuration(duration);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animation) {
animIsCurrentlyPlaying=false;
if(mCurrentRadius!=mTargetRadius){
revealModified(view, 100);
}
}
});
animIsCurrentlyPlaying=true;
anim.start();
}
}
the method will be excecuted every time you move your finger ever so slightly. I set the duration to 100 ms but that can be shorter if you find its not reactive enough. anytime you move your finger a flag is set so no other animations can be started on top and the transition is always smooth.if you move your finger while an anim is playing then revealModified will be reexecured in the if statement within onAnimationEnd.
let me know if it works and especially if this is what you wanted
I am developing two different animations as shown below.
The first animation I was able to develop using Object animator and FrameLayout. Basically, I created the two rectangles and the candlesticks inside a gravity-centered frame layout. When made to start the animation, I used the object animator to move them on either side and return to the initial position.
My second animation is the issue point here. In this animation, the rectangles will go on a circular path from their initial point and return back to their initial point. During this course, the candles need to move in sync with the rectangle giving a spring effect (or) kind of a sliced rectangle.
My initial idea was to re-use the first animation and just as the first animation runs, I would run a rotate animation on the frameLayout view.
But that doesn't seem to work wherein rotation works but the translation doesn't.
Can someone say if I have taken the right approach or should I use some other way to achieve this?
Adding the code that I have tried, but didn't work.
Animation anim = new RotateAnimation(0.0f, 360.0f, pivotX, pivotY);
anim.setDuration(500);
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
for(View view : mCandleSticks) {
ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "translationX", view.getX(), targetPos, view.getX());
anim1.setDuration(500);//set duration
anim1.start();//start animation
}
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
});
frameLayout.startAnimation(anim);
Thanks in advance.
I am trying to learn a little bit about animations and so decided to get an ImageView to go across the screen from left to right, which I achieved with:
TranslateAnimation mAnimation = new TranslateAnimation(0, 180, 0, 0);
mAnimation.setDuration(1000);
mAnimation.setFillAfter(true);
mAnimation.setRepeatCount(-1);
mAnimation.setRepeatMode(Animation.REVERSE);
myImageView.setAnimation(mAnimation);
So, I decided to take on a new challenge. Once the image has gone from left to right, I wanted to try and get the image to return to the start position by arching back from right to left to its starting position (basically, a complete semi-circle). However, once the image has reached the end of the x-axis at position 180, I get stuck.
After reading about different methods for animation, I opted to try out ValueAnimator:
ValueAnimator animator = ValueAnimator.ofFloat(0, 1); // values from 0 to 1
animator.setDuration(1000); // 1 seconds duration from start to end position
animator.setRepeatCount(-1);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) (animation.getAnimatedValue());
myImageView.setTranslationX((float)(180.0 * Math.sin(value*Math.PI)));
myImageView.setTranslationY((float)(80.0 * Math.cos(value*Math.PI)));
}
});
animator.addListener(new AnimatorListenerAdapter()
{
#Override
public void onAnimationEnd(Animator animation)
{
// done
}
});
animator.start();
However, I am unable to get the positioning correct. I do have a semi-circle (good), however, it is moving the image like a backward C rather than a straight line left to right and then arching back to its starting position (bad).
I am not sure if I should be using AnimatorSet to link the left to right movement and the arching back movement together to create one whole animation...
Can anyone give me some guidance on how to achieve what I have described?
Hello Stack community,
I want to change the position of text view.
I have tried several ways, but for all of them, the view position is set back to original position.
The ways I tried,
// 1. way
someText.setX(newPositionX);
someText.setY(newPositionY);
// 2.way
someText.x(newPositionX).y(newPositionY).setDuration(0).start();
// 3.way
someText.animate().x(newPositionX).y(newPositionY).setDuration(0).start();
//4.way
ObjectAnimator objectAnimatorX= ObjectAnimator.ofFloat(someText, "translationX", oldPositionX, newPositionX);
ObjectAnimator objectAnimatorY= ObjectAnimator.ofFloat(someText, "translationY", oldPositionY, newPositionY);
objectAnimatorX.setDuration(0);
objectAnimatorX.start();
objectAnimatorY.setDuration(0);
objectAnimatorY.start();
// 5.way
ObjectAnimator animX = ObjectAnimator.ofFloat(someText, "x", newPositionX);
ObjectAnimator animY = ObjectAnimator.ofFloat(someText, "y", newPositionY);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
For all these ways, it is reset to original place.
However when I use animate function in onTouch event eg. someText.animate().x(newPositionX).y(newPositionY).setDuration(0).start();
it does not set to original coordinate, the change is permanent. This what i want but I want to do this, without touch event, namely some other part of the code which uses some other events.
My questions are
Why the change is permanent when I use onTouch event while it is temporary when I dont use onTouch event?
How can I achieve to set Text position permanently?
Lastly my xml is like this for textView
`<TextView
android:id="#+id/latinBigTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#drawable/border_text"
android:text="Initial text " />`
Somehow I have fixed. I have not still got the best results but it updates permanently. Most probably because of my calculations but for now it seems that the position update remains. I have used following code:
TranslateAnimation anim = new TranslateAnimation(0, deltaX, 0, deltaY);
anim.setDuration(0);
anim.setFillAfter(true);
anim.setFillEnabled(true);
anim.setAnimationListener(new Animation.AnimationListener()
{
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
int l = newPosX;
int t = newPosY;
int r = SomeText.getWidth() + newPosX;
int b = SomeText.getHeight() + newPosY;
latinText.layout(l, t, r, b);
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
});
latinText.startAnimation(anim);
Where SomeText is the textView which I want to move permanently and dynamically. DeltaX and DeltaY is the amount of pixel that I want to move.
(l, r, t, b) means (left, top, right, bottom) of the view.
I hope it helps other people who have faced the same problem.
However I have still no answer for my first question. If anyone knows and tells, I would be very appreciated.
I have a button that I want to put on a 45 degree angle. For some reason I can't get this to work.
Can someone please provide the code to accomplish this?
API 11 added a setRotation() method to all views.
You could create an animation and apply it to your button view. For example:
// Locate view
ImageView diskView = (ImageView) findViewById(R.id.imageView3);
// Create an animation instance
Animation an = new RotateAnimation(0.0f, 360.0f, pivotX, pivotY);
// Set the animation's parameters
an.setDuration(10000); // duration in ms
an.setRepeatCount(0); // -1 = infinite repeated
an.setRepeatMode(Animation.REVERSE); // reverses each repeat
an.setFillAfter(true); // keep rotation after animation
// Aply animation to image view
diskView.setAnimation(an);
Extend the TextView class and override the onDraw() method. Make sure the parent view is large enough to handle the rotated button without clipping it.
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.rotate(45,<appropriate x pivot value>,<appropriate y pivot value>);
super.onDraw(canvas);
canvas.restore();
}
I just used the simple line in my code and it works :
myCusstomView.setRotation(45);
Hope it works for you.
One line in XML
<View
android:rotation="45"
... />
Applying a rotation animation (without duration, thus no animation effect) is a simpler solution than either calling View.setRotation() or override View.onDraw method.
// substitude deltaDegrees for whatever you want
RotateAnimation rotate = new RotateAnimation(0f, deltaDegrees,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// prevents View from restoring to original direction.
rotate.setFillAfter(true);
someButton.startAnimation(rotate);
Rotating view with rotate() will not affect your view's measured size. As result, rotated view be clipped or not fit into the parent layout. This library fixes it though:
https://github.com/rongi/rotate-layout
Joininig #Rudi's and #Pete's answers. I have created an RotateAnimation that keeps buttons functionality also after rotation.
setRotation() method preserves buttons functionality.
Code Sample:
Animation an = new RotateAnimation(0.0f, 180.0f, mainLayout.getWidth()/2, mainLayout.getHeight()/2);
an.setDuration(1000);
an.setRepeatCount(0);
an.setFillAfter(false); // DO NOT keep rotation after animation
an.setFillEnabled(true); // Make smooth ending of Animation
an.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {}
#Override
public void onAnimationRepeat(Animation animation) {}
#Override
public void onAnimationEnd(Animation animation) {
mainLayout.setRotation(180.0f); // Make instant rotation when Animation is finished
}
});
mainLayout.startAnimation(an);
mainLayout is a (LinearLayout) field
As mentioned before, the easiest way it to use rotation available since API 11:
android:rotation="90" // in XML layout
view.rotation = 90f // programatically
You can also change pivot of rotation, which is by default set to center of the view. This needs to be changed programatically:
// top left
view.pivotX = 0f
view.pivotY = 0f
// bottom right
view.pivotX = width.toFloat()
view.pivotY = height.toFloat()
...
In Activity's onCreate() or Fragment's onCreateView(...) width and height are equal to 0, because the view wasn't measured yet. You can access it simply by using doOnPreDraw extension from Android KTX, i.e.:
view.apply {
doOnPreDraw {
pivotX = width.toFloat()
pivotY = height.toFloat()
}
}
if you wish to make it dynamically with an animation:
view.animate()
.rotation(180)
.start();
THATS IT
#Ichorus's answer is correct for views, but if you want to draw rotated rectangles or text, you can do the following in your onDraw (or onDispatchDraw) callback for your view:
(note that theta is the angle from the x axis of the desired rotation, pivot is the Point that represents the point around which we want the rectangle to rotate, and horizontalRect is the rect's position "before" it was rotated)
canvas.save();
canvas.rotate(theta, pivot.x, pivot.y);
canvas.drawRect(horizontalRect, paint);
canvas.restore();
fun rotateArrow(view: View): Boolean {
return if (view.rotation == 0F) {
view.animate().setDuration(200).rotation(180F)
true
} else {
view.animate().setDuration(200).rotation(0F)
false
}
}
That's simple,
in Java
your_component.setRotation(15);
or
your_component.setRotation(295.18f);
in XML
<Button android:rotation="15" />