Asaf
Asaf

Reputation: 2035

Translate animation causes a change in the view's fill position, but not in layout

I'm trying to create an animation in which a LinearLayout rises up (TranslateAnimation) and the fragment within the LinearLayout changes into another (fadein/fadeout).

I managed to get it to work perfectly, but the only problem is that the layout of the LinearLayout stays at the same position (although the "fill" does rise and stay that way). It results in that I can't click any view inside the fragment that is outside the previous position of the LinearLayout.

How can I make the layout 'move up' to match the new position of the fill?

Current animation logic

        final int AmountToMove = -(desiredHeight - mLL.getHeight());
        Animation animation = new TranslateAnimation(0, 0, 0, AmountToMove);
        animation.setDuration(1000);
        animation.setInterpolator(this,
                android.R.anim.anticipate_overshoot_interpolator);
        animation.setFillAfter(true);
        
        if(CARD_STATE == 0)
            mLL.startAnimation(animation);

Image with layout bounds

enter image description here

Edit 1: Image of the layout BEFORE the animation is fired

enter image description here

Edit 2: Another trial at achieving the desired effect

animation.setAnimationListener(new TranslateAnimation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animation animation) {
                LinearLayout.LayoutParams Lp = (LinearLayout.LayoutParams) mLL.getLayoutParams();
                Lp.bottomMargin += AmountToMove;
                mLL.setLayoutParams(Lp);
            }
        });

This too, did not work.

Upvotes: 0

Views: 1424

Answers (2)

Asaf
Asaf

Reputation: 2035

In the end I managed to achieve my desired result, although not perfectly. Then I came up with another solution that does the job perfectly, but requires a sacrifice to be made hehe.

Solution #1

What I did was simply use LinearLayout.offsetTopAndBottom(int), where the parameter is the same amount of pixels you provided the TranslateAnimation object.
Also note that for this to work you must set animation.setFillBefore(true) and NOT .setFillAfter(true)!

        animation.setFillBefore(true);

        animation.setAnimationListener(new TranslateAnimation.AnimationListener() {
            
            @Override
            public void onAnimationStart(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void onAnimationEnd(Animation animation) {
                mLL.offsetTopAndBottom(AmountToMove);
            }
        });

The problem with this solutions is that there is a flicker at the end of the animation when the view is actually being pushed up.
I'm not sure, but I also think it moves around weirdly when the keyboard opens, thus making this solution that that efficient. I suggest only using it when it satisfies your need in terms of the animation fluidity, and only on views without any EditText views or any other input-requiring views.

I think that the thing is TranslateAnimation moves pixels (or the fill of the view) and not the view itself, which is why I was personally confused at first.

Solution #2

My real solution (and also the second solution), however, was to switch to ObjectAnimator instead of the regular Animation object, and animate the translateY property of the view.
It now works just as I wanted it to, no hiccups, no flickers, no problem with the keyboard, no problem editing views like EditText.

Unfortunately, though, ObjectAnimator is only supported in APIs 11+, but you can find a lower-api-compatible version of it on http://nineoldandroids.com/.

Personally, it got me wondering how many devices running API 8 are there anyways?
Apparently, as of writing this answer (July 10th, 2014), only 13.5 percent of the android devices worldwide run it, and this number only decreases with time.

Considering that, I decided to only support APIs 11+ in my app, because a lot of functionality that is critical to me was added in that version, and I do not see a reason to make the process of building the application any more difficult by trying to make the app compatible with API 8 using workarounds and a mix of libraries.
Saves a lot of time and head scratching!

Here is some code to demonstrate the usage of ObjectAnimator for translation animations:

        final int AmountToMove = -(desiredHeight - mLL.getHeight());
        ObjectAnimator objAnim = ObjectAnimator.ofFloat(mLL, "translationY", AmountToMove);
        
        objAnim.setDuration(1000);
        objAnim.setInterpolator(new OvershootInterpolator());
        
        objAnim.start();

Please note that is only my own personal opinion, which takes in mind my specific app and its target audience. Before making any rash decisions, I suggest weighing the pros against the cons of the switch, and deciding according to your own needs.

One last thing, please also like the other answer (written by Gil) as it helped me to come up with the first solution, and I appreciate that (I would've also marked it as the chosen answer but I wouldn't want to confuse or mislead other users who share the same kind of problem/confusion).

Upvotes: 0

Gil Moshayof
Gil Moshayof

Reputation: 16761

If you need the view to vanish after the animation is complete, then simply set the visibility as gone when the animation is finished:

animation.setAnimationListener(new TranslateAnimation.AnimationListener() 
        {           
            @Override
            public void onAnimationStart(Animation animation) { }

            @Override
            public void onAnimationRepeat(Animation animation) { }

            @Override
            public void onAnimationEnd(Animation animation) 
            {           
                mLL.setVisibility(View.GONE);
            }
        });

if you need to just make it move a bit, try adjusting its layout params (margin) once the animation is finished instead of just changing the visibility.

Upvotes: 1

Related Questions