Im trying to make an Imageview (a ball) move around the layout and bounce a number of times before stoping when a buttom is pressed. The probles is that although the logcat says its happening, i dont see it moving.
Here it is
public class BallPhisics {
int x =400;
int y = 0;
boolean bounceX = false;
boolean bounceY= false;
int counter =0;
ImageView object;
public BallPhisics(ImageView i){
object=i;
}
public void applyMovement() {
while (true) {
object.setLeft((int) object.getX()+x); //i know i shouldnt use pixels
Log.d("EVENT", "X moved"); Log.d("Ended",Integer.toString(object.getLeft()));
object.setBottom((int)(object.getY() + y));
Log.d("EVENT", "Y moved");
try {
Thread.sleep(1000);
Log.d("EVENT", "Time 1 used");
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
if (object.getX()<=50||(object.getRight()<=50)){
bounceX =true;
break;
}
if (object.getY()<=50||object.getTop()<=50){
bounceY=true;
break;
}
}
this.bouncing();
}
public void bouncing(){
Log.d("EVENT", "Bouncing!!");
if (bounceX&&bounceY){
x=-x;
y=-y;
}
else if (bounceX){
x=-x;
y=(int)(Math.random()*100- 50 +y);
}
else if (bounceY) {
x = (int) (Math.random() * 100 - 50 + x);
y = -y;
}
counter++;
if(counter==5){return;}
this.applyMovement();
}
And on mainActivity the onclick event.
public void StartBall (View view){
ImageView imageview=(ImageView) findViewById(R.id.imageView);
BallPhisics phisics = new BallPhisics(imageview);
Log.d("EVENT", Integer.toString(imageview.getLeft() )+" before");
phisics.applyMovement();
Log.d("EVENT",Integer.toString(imageview.getLeft())+" after" );
}
Sorry if it is a lot of reading. By the way does anyone knows the proper way of moving a view?
Thanks in advance
First of all to move a View it's probably not a good idea to use setLeft (int left) or setBottom (int bottom) because this method is meant to be called by the layout system and should not generally be called otherwise, according to the documentation.
To move a View dynamically you should take a look at LayoutParams or setX (float x) and setY (float Y), if you can limit your support to Honeycomb (API Level 11).
Regardless which one you use, you may find it difficult to achieve a smooth movement. Therefore I recommend you to use the view animation system to perform tweened animation of your ImageView.
Below you will find an example of a chain animation that moves an ImageView starting from left to right with an arbitrary y-coordinate. After each translation from one x-border to the next, the onAnimationEnd will be called and start an animation in the other direction.
public class MainActivity extends Activity {
//the ImageView you want to animate
private ImageView imageview;
private Animation mAnimation;
//The coordinates the view moved to in the last animation
private float lastX=0;
private float lastY=0;
private float secondlastY;
//Listener that implements a translate animation chain
AnimationListener animationListener=new AnimationListener() {
#Override
public void onAnimationEnd(Animation animation) {
float newY=(float)(Math.random()*0.75);
//this prevents that we move back to the position we came from
// to get a more natural bounce animation
while(newY<=secondlastY+0.15 && newY>=secondlastY-0.15){
newY=(float)(Math.random()*0.75);
}
if(lastX==0.75f){
//test if we are on the right border of the parent
mAnimation=newAnimation(lastX,lastY,0f,newY);
mAnimation.setAnimationListener(animationListener);
lastX=0f;
}else if(lastX==0.0f){
//test if we are on the left border of the parent
mAnimation=newAnimation(lastX,lastY,0.75f,newY);
mAnimation.setAnimationListener(animationListener);
lastX=0.75f;
}
secondlastY=lastY;
lastY=newY;
imageview.startAnimation(mAnimation);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageview=(ImageView) findViewById(R.id.imageView);
//the coordinates the first animation should move to
lastY=(float)(Math.random()*0.75);
lastX=0.75f;
mAnimation=newAnimation(0f,0f,lastX,lastY);
mAnimation.setAnimationListener(animationListener);
imageview.startAnimation(mAnimation);
}
//Method that returns a new animation with given start and end coordinates
private Animation newAnimation(float startX, float startY, float endX, float endY){
Animation mAnimation = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_PARENT, startX,
TranslateAnimation.RELATIVE_TO_PARENT, endX,
TranslateAnimation.RELATIVE_TO_PARENT, startY,
TranslateAnimation.RELATIVE_TO_PARENT, endY );
mAnimation.setDuration(2500);
mAnimation.setRepeatCount(0);
mAnimation.setRepeatMode(Animation.REVERSE);
mAnimation.setFillAfter(true);
return mAnimation;
}
}
Note:
The translation animation is relative to the parents width and height. If you move the View all the way to x=1.0 or y=1.0 you will move parts of the view out of the parent layout. Because it's sufficient for this example I chose to set 0.75 to the max position in either direction. But you should probably set this dynamically in regards to the width and height of your ImageView and ParentLayout.
Related
I'm implementing an Animation for one menu in my project.
The animation itself is ok, the menu enters and exit just as I wanted: Slide from left to right and right to left, however...
If the entire view is OUT of the screen, then it NEVER comes back egain! If, at least one pixel is still inside the screen, then it comes back normally.
I belive that Android is disposing the layout, and not caring about it after out of the screen bounds. I tried to place a setVisibility(VISIBLE) but it also didn't worked.
Here is the code:
public class ChwaziMenuAnimation extends Animation{
float posStart = 0;
float posTarget = 100;
int getCurrentPosition(){
RelativeLayout.LayoutParams rootParam =
(RelativeLayout.LayoutParams) rootView.getLayoutParams();
return rootParam.leftMargin;
}
public void setTarget(float target){
// Save current position
posStart = getCurrentPosition();
posTarget = target;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
RelativeLayout.LayoutParams rootParam =
(RelativeLayout.LayoutParams) rootView.getLayoutParams();
// Calculate current position
rootParam.leftMargin = (int) ((posTarget - posStart) * interpolatedTime + posStart);
rootView.setLayoutParams(rootParam);
}
/*
* Since we will be animating the margin, the bounds will always change
*/
public boolean willChangeBounds() {
return true;
};
};
And how I initialize the animation:
public void appear(){
Log.i(TAG, "appear");
menuAnimation.setTarget(0);
menuAnimation.setDuration(750);
rootView.clearAnimation();
rootView.startAnimation(menuAnimation);
}
public void disapear(){
Log.i(TAG, "disapear");
menuAnimation.setTarget(-400);
menuAnimation.setDuration(750);
rootView.startAnimation(menuAnimation);
}
I encountered the same problem, my workaround so far is to extend the view bounds to at least one pixel on the displayable area, and make that part transparent. Ugly, but for me it seems to work.
To make it even more weird: the view did not disappear, when shifted out the right side of the screen, only when shifted out the left side of the screen. But that might be device dependent.
I am planning to perform a Rotating Animation on a two directional scroll view (see link). I have got the effect of the 2 directional scroll view, but now when i perform a Filp3D Type of animation (see link) i am getting a rotating window !
i actually wanted to perform a rotation around x-axis animation and then perform successive animation.
My Code is :
public class MainActivity extends Activity {
ScrollView sv;
WScrollView hsv;
Animation anim;
RelativeLayout rl;
Button b1,b2,b3;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
/*BIDIRECTIONAL SCROLLVIEW*/
sv = new ScrollView(this);
hsv = new WScrollView(this);
hsv.sv = sv;
/*END OF BIDIRECTIONAL SCROLLVIEW*/
rl = new RelativeLayout(this);
rl.setBackgroundResource(R.drawable.interactivemap);
sv.addView(rl, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
hsv.addView(sv, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT /*or FILL_PARENT if API < 8*/));
setContentView(hsv);
Window window = getWindow();
window.setLayout(0, LayoutParams.WRAP_CONTENT);
/* Animation Effects : Stage I */
applyRotation(0, 45);
/* Animation Effects : Stage II */
/* Still to perform ! */
}
private void applyRotation(final float start, final float end) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
final float centerX = hsv.getWidth() / 2.0f + hsv.getLeft();
final float centerY = hsv.getHeight() / 2.0f + hsv.getTop();
Log.e("center-x center-y ",centerX+" "+centerY+"found");
final Flip3dAnimation rotation = new Flip3dAnimation(start, end, centerX, centerY);
rotation.setDuration(2000);
rotation.setFillAfter(true);
rotation.setInterpolator(new LinearInterpolator());
hsv.startAnimation(rotation);
}
}, 10);
}
}
Output :
1) The output is sometimes an animation around a different axis and then it becoming invisible, as if i have not put any setFillAfter(true), which happens to present in the Flip3dAnimation Class as shown in the two directional scroll view link.
2) The Animation comes and persists but only the original window is rotated in the 3d space, as if a card has flip 45 degrees !
I need the second Output with any window restrictions, as i have used a scroll view (2d), i want to be visible like a 2d plane as in a game like AOE (Age of Empires, seriously, people play games :D), or for non-games, a open 2d surface.
Please guide me for achieving the same, also please direct me to some source code which performs complex 2d animations for API-8 or 9.
I have created a 3D flip of a view using this android tutorial
However, I have done it programmatically and I would like to do it all in xml, if possible. I am not talking about simply shrinking a view to the middle and then back out, but an actual 3D flip.
Is this possible via xml?
Here is the answer, though it only works with 3.0 and above.
1) Create a new resources folder called "animator".
2) Create a new .xml file which I will call "flipping". Use the following xml code:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0" android:valueTo="360" android:propertyName="rotationY" >
</objectAnimator>
No, the objectAnimator tags do not start with an uppercase "O".
3) Start the animation with the following code:
ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(mContext, R.animator.flipping);
anim.setTarget(A View Object reference goes here i.e. ImageView);
anim.setDuration(3000);
anim.start();
I got all this from here.
Since the answers to this question are fairly dated, here is a more modern solution relying on ValueAnimators.
This solution implements a true, visually appealing 3D-flip, because it not just flips the view, but also scales it while it is flipping (this is how Apple does it).
First we set up the ValueAnimator:
mFlipAnimator = ValueAnimator.ofFloat(0f, 1f);
mFlipAnimator.addUpdateListener(new FlipListener(frontView, backView));
And the corresponding update listener:
public class FlipListener implements ValueAnimator.AnimatorUpdateListener {
private final View mFrontView;
private final View mBackView;
private boolean mFlipped;
public FlipListener(final View front, final View back) {
this.mFrontView = front;
this.mBackView = back;
this.mBackView.setVisibility(View.GONE);
}
#Override
public void onAnimationUpdate(final ValueAnimator animation) {
final float value = animation.getAnimatedFraction();
final float scaleValue = 0.625f + (1.5f * (value - 0.5f) * (value - 0.5f));
if(value <= 0.5f){
this.mFrontView.setRotationY(180 * value);
this.mFrontView.setScaleX(scaleValue);
this.mFrontView.setScaleY(scaleValue);
if(mFlipped){
setStateFlipped(false);
}
} else {
this.mBackView.setRotationY(-180 * (1f- value));
this.mBackView.setScaleX(scaleValue);
this.mBackView.setScaleY(scaleValue);
if(!mFlipped){
setStateFlipped(true);
}
}
}
private void setStateFlipped(boolean flipped) {
mFlipped = flipped;
this.mFrontView.setVisibility(flipped ? View.GONE : View.VISIBLE);
this.mBackView.setVisibility(flipped ? View.VISIBLE : View.GONE);
}
}
That's it!
After this setup you can flip the views by calling
mFlipAnimator.start();
and reverse the flip by calling
mFlipAnimator.reverse();
If you want to check if the view is flipped, implement and call this function:
private boolean isFlipped() {
return mFlipAnimator.getAnimatedFraction() == 1;
}
You can also check if the view is currently flipping by implementing this method:
private boolean isFlipping() {
final float currentValue = mFlipAnimator.getAnimatedFraction();
return (currentValue < 1 && currentValue > 0);
}
You can combine the above functions to implement a nice function to toggle the flip, depending on if it is flipped or not:
private void toggleFlip() {
if(isFlipped()){
mFlipAnimator.reverse();
} else {
mFlipAnimator.start();
}
}
That's it! Simple and easy. Enjoy!
I have created a simple program for creating flip of view like :
In Activity you have to create this method, for adding flip_rotation in view.
private void applyRotation(View view)
{
final Flip3dAnimation rotation = new Flip3dAnimation(view);
rotation.applyPropertiesInRotation();
view.startAnimation(rotation);
}
for this, you have to copy main class used to provide flip_rotation.
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class Flip3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private Camera mCamera;
public Flip3dAnimation(View view) {
mFromDegrees = 0;
mToDegrees = 720;
mCenterX = view.getWidth() / 2.0f;
mCenterY = view.getHeight() / 2.0f;
}
#Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
public void applyPropertiesInRotation()
{
this.setDuration(2000);
this.setFillAfter(true);
this.setInterpolator(new AccelerateInterpolator());
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees
+ ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
Log.e("Degree",""+degrees) ;
Log.e("centerX",""+centerX) ;
Log.e("centerY",""+centerY) ;
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
The tutorial or the link by om252345 don't produce believable 3D flips. A simple rotation on the y-axis isn't what's done in iOS. The zoom effect is also needed to create that nice flip feel. For that, take a look at this example.
There is also a video here.
One of the better solution to flip the image with out use of the resource animation , is as follow:-
ObjectAnimator animation = ObjectAnimator.ofFloat(YOUR_IMAGEVIEW, "rotationY", 0.0f, 360f); // HERE 360 IS THE ANGLE OF ROTATE, YOU CAN USE 90, 180 IN PLACE OF IT, ACCORDING TO YOURS REQUIREMENT
animation.setDuration(500); // HERE 500 IS THE DURATION OF THE ANIMATION, YOU CAN INCREASE OR DECREASE ACCORDING TO YOURS REQUIREMENT
animation.setInterpolator(new AccelerateDecelerateInterpolator());
animation.start();
The simplest way to do it is using ViewPropertyAnimator
mImageView.animate().rotationY(360f);
Using the fluent interface you can build more complex and exciting animation.
E.g. you can enable hardware acceleration just call withLayer() method(API 16). More here
If you want to figure out how to create 3d flick animation, please follow here and here
I implemended my own solution only for a research. It includes: cancelation, accelleration, support API >= 15 and is based on Property Animation.
The entire animation includes 4 parts, 2 for each side.
Every objectAnimator has a listener that defines current animation index and represents an image in the onAnimationStart and current play time value in the onAnimationCancel.
It looks like
mQuarterAnim1.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
mQuarterCurrentAnimStartIndex = QUARTER_ANIM_INDEX_1;
mImageView.setImageResource(mResIdFrontCard);
}
#Override
public void onAnimationCancel(Animator animation) {
mQuarterCurrentAnimPlayTime = ((ObjectAnimator) animation).getCurrentPlayTime();
}
});
For start set call
mAnimatorSet.play(mQuarterAnim1).before(mQuarterAnim2)
If AnimatorSet was canceled we can calculate delta and run the reverse animation relying on the current index animation and the current play time value.
long degreeDelta = mQuarterCurrentAnimPlayTime * QUARTER_ROTATE / QUARTER_ANIM_DURATION;
if (mQuarterCurrentAnimStartIndex == QUARTER_ANIM_INDEX_1) {
mQuarterAnim4.setFloatValues(degreeDelta, QUARTER_FROM_1);
mQuarterAnim4.setDuration(mQuarterCurrentAnimPlayTime);
mAnimatorSet.play(mQuarterAnim4);
}
A full code snippet you can find here
Just put the view which you're going to animate it in place of viewToFlip.
ObjectAnimator flip = ObjectAnimator.ofFloat(viewToFlip, "rotationY", 0f, 360f); // or rotationX
flip.setDuration(2000); // 2 seconds
flip.start();
Adding to A. Steenbergen's great answer. When flipping the same view (updating a TextView for example) I removed the View.Visibility change in the constructor in order to keep the transition smoother.
public FlipListener(final View front, final View back) {
this.mFrontView = front;
this.mBackView = back;
}
I'm using an xml defined animation to slide a view off the screen. The problem is, as soon as the animation completes it resets to its original position. I need to know how to fix this. Here's the xml:
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="#android:anim/accelerate_interpolator">
<translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="500"/></set>
Here's the Java that I use to call it:
homeScrn = (View)findViewById(R.id.homescreen);
slideLeftOut = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
//Set Click Listeners For Menu
btnHelp.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
LayoutInflater.from(getApplicationContext()).inflate(R.layout.help, (ViewGroup)findViewById(R.id.subpage), true);
homeScrn.startAnimation(slideLeftOut);
}
});
So basically what happens is I inflate a view underneath one. Then I animate the view on top off to the left. As soon as it gets off screen and the animation is finished it resets its position back.
Finally got a way to work around,the right way to do this is setFillAfter(true),
if you want to define your animation in xml then you should do some thing like this
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/decelerate_interpolator"
android:fillAfter="true">
<translate
android:fromXDelta="0%"
android:toXDelta="-100%"
android:duration="1000"/>
</set>
you can see that i have defined filterAfter="true" in the set tag,if you try to define it in translate tag it won't work,might be a bug in the framework!!
and then in the Code
Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_out);
someView.startAnimation(anim);
OR
TranslateAnimation animation = new TranslateAnimation(-90, 150, 0, 0);
animation.setFillAfter(true);
animation.setDuration(1800);
someView.startAnimation(animation);
then it will surely work!!
Now this is a bit tricky it seems like the view is actually move to the new position but actually the pixels of the view are moved,i.e your view is actually at its initial position but not visible,you can test it if have you some button or clickable view in your view(in my case in layout),to fix that you have to manually move your view/layout to the new position
public TranslateAnimation (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
new TranslateAnimation(-90, 150, 0, 0);
now as we can see that our animation will starts from -90 x-axis to 150 x-axis
so what we do is set
someView.setAnimationListener(this);
and in
public void onAnimationEnd(Animation animation)
{
someView.layout(150, 0, someView.getWidth() + 150, someView.getHeight());
}
now let me explain public void layout (int left, int top, int right, int botton)
it moves your layout to new position first argument define the left,which we is 150,because translate animation has animated our view to 150 x-axis, top is 0 because we haven't animated y-axis,now in right we have done someView.getWidth() + 150 basically we get the width of our view and added 150 because we our left is now move to 150 x-axis to make the view width to its originall one, and bottom is equals to the height of our view.
I hope you people now understood the concept of translating, and still you have any questions you can ask right away in comment section,i feel pleasure to help :)
EDIT Don't use layout() method as it can be called by the framework when ever view is invalidated and your changes won't presist, use LayoutParams to set your layout parameters according to your requirement
The animations are working as expected. Just because you animated something does not mean you actually moved it. An animation only affects drawn pixels during the animation itself, not the configuration of the widgets.
You need to add an AnimationListener, and in the onAnimationEnd() method, do something that makes your move permanent (e.g., removes the view on top from its parent, marks the view on top as having visibility GONE).
I might be a bit late in replying, but I have the solution for this:
Just add android:fillAfter="true"
in your xml
You can use
fillAfter=true
fillEnabled=true
your View will'not reset to original position, but if you have some buttons or something else in your view , their position will not change.
You must use ObjectAnimator , it works from API 11 level . It changes View position automatic,
here is the example
ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(mContent_container, "translationX", startX, endX);
objectAnimator.setDuration(1000);
objectAnimator.start();
Thanks JUL for his answer
If your app not found object animator, change the API level from Project -> properties -> Android , than import android.animation.ObjectAnimator;
Regards Hayk
You need to add this command:
final Animation animSlideLeft = new TranslateAnimation(0, -80, 0,-350, 0, 0, 0, 0);
animSlideLeft.setFillAfter(true);
I went to the same question and solved it with setting the marginLeft at OnAnimationEnd()..
MarginLayoutParams params = (MarginLayoutParams) this.getLayoutParams();
params.setMargins(this.getWidth()*position, 0,0,0);
this.setLayoutParams(params);
This problem has bothered for more than a week.
And Muhammad's answer pointed me a strightforward way to solve it.
I used sever layout to implement side menus with animation.
"view.layout will not be persistent. You should use LayoutParams, which will be persistent."
Here's my solution:
(Use what kind of LayoutParameter is depended on your own parent's layout):
#Override
public void onAnimationEnd(Animation animation) {
FrameLayout.LayoutParams layoutParams=new FrameLayout.LayoutParams(screenWidth, screenHeight);
layoutParams.setMargins((int) -(screenWidth * RATE), 0,
(int) (screenWidth - screenWidth * RATE), screenHeight);
for(View v:views) {
v.setLayoutParams(layoutParams);
//v.layout((int) -(screenWidth * RATE), 0, (int) (screenWidth - screenWidth * RATE), screenHeight);
v.clearAnimation();
}
}
fillAfter = true will do the job to transform view to new animation positon.
rotateAnimation.fillAfter = true;
If anybody interested to move arrow_up & arrow_down animations for a recycle view expands/collapse behavior.
Initial state: Collapse
call the method with a child visibility flag(View.VISIBLE/View.GONE).
Below is code belongs to animations.
fun setArrowImage(imageView: ImageView, childVisibility: Int) {
val angleRange = if (childVisibility == View.VISIBLE) Pair(0F, 180F) else
Pair(100f, 0F)
val rotateAnimation = RotateAnimation(
angleRange.first, angleRange.second,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
)
rotateAnimation.duration = 300
rotateAnimation.fillAfter = true;
imageView.startAnimation(rotateAnimation)
}
Use helper methods below to easily animate your view. Then you can animate and reset after completion like this:
// Animate the view:
fly(
view1,
(float) 0,
(float) 0,
(float) 0,
(float) 1000,
250,
null,
null,
() -> {
// Return the view to initial position on animation end:
fly(
view1,
(float) 0,
(float) 0,
(float) 0,
(float) 0,
0);
return null;
});
Helper methods:
/**
* Translation of a view.
*/
public static void fly(
View view,
float fromXDelta,
float toXDelta,
float fromYDelta,
float toYDelta,
int duration) {
fly(
view,
fromXDelta,
toXDelta,
fromYDelta,
toYDelta,
duration,
null,
null,
null);
}
/**
* Translation of a view with handlers.
*
* #param view A view to animate.
* #param fromXDelta Amount to shift by X axis for start position.
* #param toXDelta Amount to shift by X axis for end position.
* #param fromYDelta Amount to shift by Y axis for start position.
* #param toYDelta Amount to shift by Y axis for end position.
* #param duration Animation duration.
* #param start On animation start. Otherwise NULL.
* #param repeat On animation repeat. Otherwise NULL.
* #param end On animation end. Otherwise NULL.
*/
public static void fly(
View view,
float fromXDelta,
float toXDelta,
float fromYDelta,
float toYDelta,
int duration,
Callable start,
Callable repeat,
Callable end) {
Animation animation;
animation =
new TranslateAnimation(
fromXDelta,
toXDelta,
fromYDelta,
toYDelta);
animation.setDuration(
duration);
animation.setInterpolator(
new DecelerateInterpolator());
animation.setFillAfter(true);
view.startAnimation(
animation);
animation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
if (start != null) {
try {
start.call();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onAnimationEnd(Animation animation) {
if (end != null) {
try {
end.call();
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Override
public void onAnimationRepeat(
Animation animation) {
if (repeat != null) {
try {
repeat.call();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
Nothing from answers worked with me; although #Muhammad Babar answer inspired me to get a solution:
He mentioned that we can layout the view to the new position upon animation end:
public void onAnimationEnd(Animation animation) {
someView.layout(150, 0, someView.getWidth() + 150, someView.getHeight());
}
Although this didn't work with me instead; I had to do that in a Handler with delayed Runnable using a delay value that is slightly less than the animation duration.
So, in my case the animation duration is 500 msec, and I used a handler on 450 mesec, so it gets triggered just before the animation ends by 50 msec.
Handler().postDelayed({
someView.layout(150, 0, someView.width + 150, someView.hight)
someView.requestLayout()
someView.forceLayout()
}, 450)
I would like to apply successive animations (say ScaleAnimation) to an ImageView showing a resource image. The animation is triggered by a button. For example, I would like to incrementally enlarge an image upon each button click.
I've set fillAfter="true" on the animation. However, all the animations start from the original state of the ImageView. It seems as if the ImageView resets its state and the animation is always the same, instead of starting from the final state of the previous animation.
What am I doing wrong?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button)findViewById(R.id.Button01);
button.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
animate();
}});
}
private void animate() {
ImageView imageView = (ImageView) findViewById(R.id.ImageView01);
ScaleAnimation scale = new ScaleAnimation((float)1.0, (float)1.5, (float)1.0, (float)1.5);
scale.setFillAfter(true);
scale.setDuration(500);
imageView.startAnimation(scale);
}
It seems as if the ImageView resets
its state and the animation is always
the same, instead of starting from the
final state of the previous animation.
Precisely! I'm sure there's a use for fillAfter="true", but I haven't figured out the point for it yet.
What you need to do is set up an AnimationListener on each Animation of relevance, and do something in the listener's onAnimationEnd() to actually persist the end state of your animation. I haven't played with ScaleAnimation so I'm not quite sure what the way to "persist the end state" would be. If this were an AlphaAnimation, going from 1.0 to 0.0, you would make the widget INVISIBLE or GONE in onAnimationEnd(), for example.
I've had the same problem and created the following code to easily use different animations. It only supports translation and alpha levels for now as I haven't used scaling, but could easily be extended to support more features.
I reset the scroll and the visibility before starting the animation, but that's just because I needed on/off animations.
And the "doEnd" boolean is there to avoid a stack overflow on the recursion (scrollTo calls onAnimationEnd for some obscure reason...)
private void setViewPos(View view, Animation anim, long time){
// Get the transformation
Transformation trans = new Transformation();
anim.getTransformation(time, trans);
// Get the matrix values
float[] values = new float[9];
Matrix m = trans.getMatrix();
m.getValues(values);
// Get the position and apply the scroll
final float x = values[Matrix.MTRANS_X];
final float y = values[Matrix.MTRANS_Y];
view.scrollTo(-(int)x, -(int)y);
// Show/hide depending on final alpha level
if (trans.getAlpha() > 0.5){
view.setVisibility(VISIBLE);
} else {
view.setVisibility(INVISIBLE);
}
}
private void applyAnimation(final View view, final Animation anim){
view.scrollTo(0, 0);
view.setVisibility(VISIBLE);
anim.setAnimationListener(new AnimationListener(){
private boolean doEnd = true;
#Override
public void onAnimationEnd(Animation animation) {
if (doEnd){
doEnd = false;
setViewPos(view, animation, anim.getStartTime() + anim.getDuration());
}
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationStart(Animation animation) {
}
});
view.startAnimation(anim);
}