3

I used some code I found for a sliding panel, and basically it works but has a small issue.

The animation doesn't work on the first time the panel gets open.

here's the code for the animation:

    TranslateAnimation anim = null;


    m_isOpen = !m_isOpen;

    if (m_isOpen) {
        setVisibility(View.VISIBLE);
        anim = new TranslateAnimation(0.0f, 0.0f, getHeight(), 0.0f);
    } else {
        anim = new TranslateAnimation(0.0f, 0.0f, 0.0f, getHeight());
        anim.setAnimationListener(new Animation.AnimationListener() {
              public void onAnimationEnd(Animation animation) {
                   setVisibility(View.GONE);
              }

              public void onAnimationRepeat(Animation animation) {
                   // not needed
              }

              public void onAnimationStart(Animation animation) {
                   // not needed
              }
        });

    }

    anim.setDuration(300);
    anim.setInterpolator(new AccelerateInterpolator(1.0f));
    startAnimation(anim);

why on the first I open the panel there's no animation but all the other ones there is?

piojo
  • 1,919
  • 4
  • 24
  • 40
  • Are you sure it doesn't work? Try to debug it of put some logs. Mabye it starts when your panel isn't visible yet or is creating it's view? – ania Nov 21 '11 at 16:58
  • It doesn't work. only after it opens for the first time, the animations works for both opening and closing – piojo Nov 21 '11 at 17:15
  • I found a kind of solution: instead of set the visibility of the panel to GONE, I just close the panel when the app starts – piojo Nov 21 '11 at 17:29

4 Answers4

6

When are you calling this? It is probably only doing it "after the first time" because the panel has not been rendered yet when you first call it, and getHeight() is returning 0. Try waiting until getHeight() has a value and start the animation then. You can also try to hardcode the value to something just to test that my theory is right.

dmon
  • 30,048
  • 8
  • 87
  • 96
  • you right. I tried with `getMeasuredHeight()` which I thought would give the height before rendering, but apparently it isn't. is there a better solution than the one I wrote in the comment above? – piojo Nov 21 '11 at 17:37
  • I don't know your context (presumably this is a widget?), if you're doing a custom View, you can check out [this question](http://stackoverflow.com/questions/2864331/how-to-get-an-android-widgets-size-after-layout-is-calculated) for a suggested alternative. – dmon Nov 21 '11 at 17:43
  • this answer to this question doesn't work for my scenario. `getHeight()` is called when the application is up and running. – piojo Nov 21 '11 at 18:00
  • well, I think the only solution is to set the animation with a fixed number instead of `getHeight` – piojo Nov 21 '11 at 18:08
  • Hopefully it's a known quantity that you can set on a constant. – dmon Nov 21 '11 at 18:56
  • Downvoted since "try waiting until getHeight() has a value" doesn't make sense until you suggest a solution on _how_ to wait. – Aleks N. May 31 '12 at 12:34
  • 1
    The easiest way to wait for a component to be ready is to use the [post](http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable)) method. – dmon Jun 05 '12 at 03:40
  • Just to elaborate on what dmon said, if the height is set to a specific value in the XML, then you can use the following code to get it: `int theLayoutHeight = theView.getLayoutParams().height; if (theLayoutHeight > 0) { theViewHeight = theLayoutHeight; }` – dazed Jan 06 '16 at 22:07
0

The way I made that code working:


final ViewGroup viewGroup = (ViewGroup) findViewById(R.id.panel);
viewGroup.setVisibility(View.VISIBLE);
final ViewTreeObserver treeObserver = viewGroup.getViewTreeObserver();

if (treeObserver.isAlive()) {
    final OnPreDrawListener l = new OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            treeObserver.removeOnPreDrawListener(this);
            TranslateAnimation anim = new TranslateAnimation(0.0f, 0.0f, viewGroup.getHeight(), 0.0f);
            anim.setDuration(1000);
            anim.setInterpolator(new AccelerateInterpolator(1.0f));
            viewGroup.startAnimation(anim);

            return false;
            }
        };
    treeObserver.addOnPreDrawListener(l);
}

That is it in its essence. Hope it helps.

Aleks N.
  • 6,051
  • 3
  • 42
  • 42
0

If it is only one view that you are trying to animate, then you can write the animation in XML like so:

<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromYDelta="-100%"
        (or)    
android:toYDelta="-100%"  
android:duration="300"/>

This way, you don't need to use getHeight(). The Java portion then looks like this:

    final View flashTitle = findViewById(R.id.flash_title);
    Animation slideDownOnscreen = AnimationUtils.loadAnimation
            (this,  R.anim.slide_down_onscreen);
    flashTitle.setAnimation(slideDownOnscreen);
    flashTitle.setVisibility(View.VISIBLE);

Alternatively, if you need to animate several views in conjunction, do the above with the first view, and animate the subsequent views in a post runnable so that getHeight() returns the correct height of the first view, like so:

    flashTitle.post(new Runnable() {
        @Override
        public void run() {
            System.out.println("NOTE    flashTitle height: "+flashTitle.getHeight());
            Animation slideDown = new TranslateAnimation(0, 0,
                    -flashTitle.getHeight(),0);
            slideDown.setDuration(300);
            flashLine.setVisibility(View.VISIBLE);
            flashLine.startAnimation(slideDown);
            //(or)flashLine.animate().translationYBy(flashTitle.getHeight()).setDuration(300);
        }
    });

You would also need to add the accelerate_interpolator as Android's default is accelerate_decelerate_interpolator.

willlma
  • 7,353
  • 2
  • 30
  • 45
  • If you also need to move a view that's currently visible out of the way, you'll need to calculate the height in `onCreate()`. See [this answer](http://stackoverflow.com/a/10134260/1902059) on how. – willlma Mar 11 '13 at 12:33
0

you can Use Delay for work it

 TranslateAnimation animate1 = new TranslateAnimation(
                    0,                 // fromXDelta
                    0,                 // toXDelta
                    0,  // fromYDelta
                    binding.saveCardview.getMeasuredHeight());                // toYDelta
                animate1.setDuration(500);
                animate1.setFillAfter(true);
                binding.saveCardview.startAnimation(animate1);

                final Handler handler = new Handler(Looper.getMainLooper());
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //Do something after 100ms
                        binding.saveCardview.setVisibility(View.GONE);
                        TranslateAnimation animate2 = new TranslateAnimation(
                                0,                 // fromXDelta
                                0,                 // toXDelta
                                binding.carCardview.getHeight(),  // fromYDelta
                                0);                // toYDelta
                        animate2.setDuration(1000);
                        animate2.setFillAfter(true);
                        binding.carCardview.startAnimation(animate2);
                    }
                }, 500);
Hesam Kh
  • 111
  • 2
  • 1