I have the following View setup in one of my Activities:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/photoLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="#+id/photoImageView"
android:src="#drawable/backyardPhoto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:scaleType="centerInside"
android:padding="45dip"
>
</ImageView>
</LinearLayout>
Without an animation set, this displays just fine. However I want to display a very simple animation. So in my Activity's onStart override, I have the following:
#Override
public void onStart() {
super.onStart();
mPhotoImageView = (ImageView) findViewById(R.id.photoImageView);
float offset = -25;
int top = mPhotoImageView.getTop();
TranslateAnimation anim1 = new TranslateAnimation(
Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, top, Animation.ABSOLUTE, offset);
anim1.setInterpolator(new AnticipateInterpolator());
anim1.setDuration(1500);
anim1.setStartOffset(5000);
TranslateAnimation anim2 = new TranslateAnimation(
Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, offset, Animation.ABSOLUTE, top);
anim2.setInterpolator(new BounceInterpolator());
anim2.setDuration(3500);
anim2.setStartOffset(6500);
mBouncingAnimation = new AnimationSet(false);
mBouncingAnimation.addAnimation(anim1);
mBouncingAnimation.addAnimation(anim2);
mPhotoImageView.setAnimation(mBouncingAnimation);
}
The problem is that when the Activity displays for the first time, the initial position of the photo is not in the center of the screen with padding around. It seems like the first frame of the animation is loaded already. Only after the animation is completed, does the photoImageView "snap" back to the intended location.
I've looked and looked and could not find how to avoid this problem. I want the photoImageView to start in the center of the screen, and then the animation to happen, and return it to the center of the screen. The animation should happen by itself without interaction from the user.
mPhotoImageView.getTop() will return 0 in onCreate(), you need to wait until after the first layout/draw has happened to get the correct position of your View.
Related
I have an image, which is a bee. I want to add an animation that make the bee look like it is randomly flying up and down within a specified area. The bee should only fly within paddingTop="200dp" to paddingTop="370dp". When it reaches the left of the screen, it should fly back to the right of the screen. And it should be non-stop moving from both sides. However, I can only make it fly or move horizontally with a specified time period.
<ImageView
android:id="#+id/bee"
android:paddingTop="200dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/beeFaceRight" />
The code in Main.java
ObjectAnimator animation;
ImageView bee;
protected void onCreate(Bundle savedInstanceState) {
bee = (ImageView) findViewById(R.id.bee);
animation = ObjectAnimator.ofFloat(bee, "translationX", 100f);
animation.setDuration(2000);
animation.start();
}
I have tried this, but it does not work.
ObjectAnimator objectX;
ObjectAnimator objectY;
AnimatorSet animatorXY;
objectX = ObjectAnimator.offFloat(bee, "translationX", 0);
objectY = ObjectAnimator.offFloat(bee, "translationY", 200);
animatorXY.playTogether(objectX, objectY);
animatorXY.setDuration(500);
animatorXY.start();
I have an image for my layout background. I want it to start moving to the right, and every frame that disappears from the right, should reappear on the left. Therefore, the image will keep moving continously with only one frame of the photo. How can I do that?
The easiest and fastest way to do so, it's to having two ImageViews in a ViewGroup and animate them with two differents animations. By getting the container's width, the first will move from its position (START) to the right edge (PARENT_WIDTH), and the second will follow from outside the container (-PARENT_WIDTH) to inside (START). Finally, make the animations repeat INFINITE will do the illusion of a real loop.
private ViewGroup parent;
private ImageView imgInner, imgOutter;
#Override
public void onCreate(...) {
...
parent = (ViewGroup) findViewById(R.id.parent_loop);
imgInner = (ImageView) findViewById(R.id.image_loop_inner);
imgOutter = (ImageView) findViewById(R.id.image_loop_outter);
...
setImageLoop();
}
private void setImageLoop() {
// Need a thread to get the real size or the parent
// container, after the UI is displayed
imgInner.post(new Runnable() {
#Override
public void run() {
TranslateAnimation outAnim =
new TranslateAnimation(
0f, parent.getWidth(), 0f, 0f);
// move from 0 (START) to width (PARENT_SIZE)
outAnim.setInterpolator(new LinearInterpolator());
outAnim.setRepeatMode(Animation.INFINITE); // repeat the animation
outAnim.setRepeatCount(Animation.INFINITE);
outAnim.setDuration(2000);
TranslateAnimation inAnim =
new TranslateAnimation(
- parent.getWidth(), 0f, 0f, 0f);
// move from out width (-PARENT_SIZE) to 0 (START)
inAnim.setInterpolator(new LinearInterpolator());
inAnim.setRepeatMode(Animation.INFINITE);
inAnim.setRepeatCount(Animation.INFINITE);
inAnim.setDuration(2000); // same duration as the first
imgInner.startAnimation(outAnim); // start first anim
imgOutter.startAnimation(inAnim); // start second anim
}
});
}
The container ViewGroup has match_parent on its width, but it could be changed, and therefore the START attribute will be replaced by something like parent.getLeft(). This layout could be a LinearLayout, a RelativeLayout, or whatever. For example, I used this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="250dp"
...>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
.../>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
.../>
</FrameLayout>
Which gives my this output (keep in mind the gif makes it look choppy when it is really not):
Updated the code:
img = (ImageView) findViewById(R.id.imageView1);
TranslateAnimation animation = new TranslateAnimation(-95.0f, 740.0f,
0.0f, 0.0f); // new TranslateAnimation(xFrom,xTo, yFrom,yTo)
animation.setDuration(5000); // animation duration
animation.setRepeatCount(5); // animation repeat count
img.startAnimation(animation); // start animation
I initialize a ViewGroup with the code below. When a button is pressed I run this.startAnimation(moveMenu). startAnimation will run the view's invalidate so it will draw something on the view. However, if I run setTranslationY in the function below then the view does not have the drawing show up properly. If I remove it then it works and slides down and then back up.
This is not the behavior I want though, I want the viewGroup to be hidden above the screen and slide down when the button is pressed, then slide back up to hide again. So this is why I need the this.setTranslationY.
void init(Context context) {
this.setBackgroundColor(Color.WHITE);
this.setTranslationY(this.height * -1); // if I remove this the drawing works
Resources r = this.getContext().getResources();
loadBitmapArray(0, r.getDrawable(R.drawable.the_drawable)) ;
show = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, height);
show.setDuration(1000);
show.setFillAfter(true);
hide = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -height);
hide.setStartOffset(show.getDuration() + 2000);
hide.setDuration(1000);
hide.setFillAfter(true);
moveMenu = new AnimationSet(true);
moveMenu.addAnimation(show);
moveMenu.addAnimation(hide);
}
To summarize and simplify my problem I try to animate a root view and then animate subview.
The first animation work well, but the second most of the time, stay in place.
The layout :
<FrameLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
The sample code :
TranslateAnimation translateAnimation=new TranslateAnimation(0, 0, 0, 300);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(true);
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) { }
#Override
public void onAnimationEnd(Animation animation) {
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setInterpolator(new LinearInterpolator());
rotateAnimation.setDuration(750);
rotateAnimation.setRepeatCount(Animation.INFINITE);
rotateAnimation.setRepeatMode(Animation.RESTART);
imageView.startAnimation(rotateAnimation);
}
#Override
public void onAnimationRepeat(Animation animation) { }
});
container.startAnimation(translateAnimation);
Sometimes I see the imageview tilted. Or rotate, but just one part.
Someone have an idea of what happen ?
I also tried with setFilterAfter(false) and move my container with setTranslationY() on onAnimationEnd but some frames are visible.
Thanks.
EDIT
In the real case, the TranslateAnimation is on a ViewPager (to QuickReturn pattern) and the RotateAnimation (to PullToRefresh pattern) inside fragment of ViewPager.
So animations is not necessarily sequential like I have done above, and strongly separated (ViewPager/Fragment).
EDIT2
I just see that the touch area don't move with the view.
I'm not saying it's going to work but let's give this a try:
Let's separate animations and not chain them with animationListener; so instead, let's use the ObjectAnimator and AnimationSet to help us sequence animations for us.
ObjectAnimator translateContainer = ObjectAnimator.ofFloat(container, "translationY", 300);
translateContainer.setDuration(2000);
ObjectAnimator rotateImage = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f);
rotateImage.setDuration(750);
AnimatorSet animSet = new AnimatorSet();
animSet.playSequentially(translateContainer, rotateImage);
animSet.start();
I expect the button move down 200px (from 0,0 to 0, 200)
and after 1 second, move up again to original position.
But following code acts like start from (0,200) to (0,400), not start from (0,0)
If I put one animation into AnimationSet, it works fine. But it works strange if I put more than two animations in the AnimationSet.
What's the problem?
public class MainActivity extends Activity {
private static final int DISTANCE = 200;
private static final long DURATION = 1000;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.button1);
TranslateAnimation aniMove = new TranslateAnimation(0, 0, 0, DISTANCE);
aniMove.setDuration(DURATION);
TranslateAnimation aniMoveBack = new TranslateAnimation(0, 0, DISTANCE, 0);
aniMoveBack.setDuration(DURATION);
aniMoveBack.setStartOffset(DURATION + 1000);
AnimationSet aniSet = new AnimationSet(true);
aniSet.addAnimation(aniMove);
aniSet.addAnimation(aniMoveBack);
btn.startAnimation(aniSet);
}
}
Here is XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
AnimationSet represents a group of Animations that should be played together. The
transformation of each individual animation are composed together into
a single transform.
AnimationSet could not be used in this way, it could combine the feature of multi animations. Using AnimationSet you can perform alpha and rotate animation at the same time.
If you want to play 2 animations in order, you should start the second animation in onAnimationEnd() of the first animation.
I found the solution.
Coordinates in TranslateAnimation() parameter is not absolute one.
Because I called setStartOffset(N), aniMoveBack animation will start from the end of aniMove coordinate.
It is relative position.
So, the code should be fixed like this..
TranslateAnimation aniMoveBack = new TranslateAnimation(0, 0, 0, -DISTANCE);