Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package androidx.fragment.app;
     17 
     18 import static org.junit.Assert.assertEquals;
     19 import static org.junit.Assert.assertFalse;
     20 import static org.junit.Assert.assertNotNull;
     21 import static org.junit.Assert.assertNull;
     22 import static org.junit.Assert.assertTrue;
     23 
     24 import android.app.Instrumentation;
     25 import android.os.Bundle;
     26 import android.os.Parcelable;
     27 import android.support.test.InstrumentationRegistry;
     28 import android.support.test.filters.MediumTest;
     29 import android.support.test.rule.ActivityTestRule;
     30 import android.support.test.runner.AndroidJUnit4;
     31 import android.util.Pair;
     32 import android.view.LayoutInflater;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.view.animation.Animation;
     36 import android.view.animation.AnimationUtils;
     37 import android.view.animation.TranslateAnimation;
     38 
     39 import androidx.annotation.AnimRes;
     40 import androidx.core.view.ViewCompat;
     41 import androidx.fragment.app.test.FragmentTestActivity;
     42 import androidx.fragment.test.R;
     43 
     44 import org.junit.Before;
     45 import org.junit.Rule;
     46 import org.junit.Test;
     47 import org.junit.runner.RunWith;
     48 
     49 import java.util.concurrent.CountDownLatch;
     50 import java.util.concurrent.TimeUnit;
     51 
     52 @MediumTest
     53 @RunWith(AndroidJUnit4.class)
     54 public class FragmentAnimationTest {
     55     // These are pretend resource IDs for animators. We don't need real ones since we
     56     // load them by overriding onCreateAnimator
     57     @AnimRes
     58     private static final int ENTER = 1;
     59     @AnimRes
     60     private static final int EXIT = 2;
     61     @AnimRes
     62     private static final int POP_ENTER = 3;
     63     @AnimRes
     64     private static final int POP_EXIT = 4;
     65 
     66     @Rule
     67     public ActivityTestRule<FragmentTestActivity> mActivityRule =
     68             new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
     69 
     70     private Instrumentation mInstrumentation;
     71 
     72     @Before
     73     public void setupContainer() {
     74         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     75         FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
     76     }
     77 
     78     // Ensure that adding and popping a Fragment uses the enter and popExit animators
     79     @Test
     80     public void addAnimators() throws Throwable {
     81         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
     82 
     83         // One fragment with a view
     84         final AnimatorFragment fragment = new AnimatorFragment();
     85         fm.beginTransaction()
     86                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
     87                 .add(R.id.fragmentContainer, fragment)
     88                 .addToBackStack(null)
     89                 .commit();
     90         FragmentTestUtil.waitForExecution(mActivityRule);
     91 
     92         assertEnterPopExit(fragment);
     93     }
     94 
     95     // Ensure that removing and popping a Fragment uses the exit and popEnter animators
     96     @Test
     97     public void removeAnimators() throws Throwable {
     98         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
     99 
    100         // One fragment with a view
    101         final AnimatorFragment fragment = new AnimatorFragment();
    102         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
    103         FragmentTestUtil.waitForExecution(mActivityRule);
    104 
    105         fm.beginTransaction()
    106                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    107                 .remove(fragment)
    108                 .addToBackStack(null)
    109                 .commit();
    110         FragmentTestUtil.waitForExecution(mActivityRule);
    111 
    112         assertExitPopEnter(fragment);
    113     }
    114 
    115     // Ensure that showing and popping a Fragment uses the enter and popExit animators
    116     @Test
    117     public void showAnimators() throws Throwable {
    118         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    119 
    120         // One fragment with a view
    121         final AnimatorFragment fragment = new AnimatorFragment();
    122         fm.beginTransaction().add(R.id.fragmentContainer, fragment).hide(fragment).commit();
    123         FragmentTestUtil.waitForExecution(mActivityRule);
    124 
    125         fm.beginTransaction()
    126                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    127                 .show(fragment)
    128                 .addToBackStack(null)
    129                 .commit();
    130         FragmentTestUtil.waitForExecution(mActivityRule);
    131 
    132         assertEnterPopExit(fragment);
    133     }
    134 
    135     // Ensure that hiding and popping a Fragment uses the exit and popEnter animators
    136     @Test
    137     public void hideAnimators() throws Throwable {
    138         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    139 
    140         // One fragment with a view
    141         final AnimatorFragment fragment = new AnimatorFragment();
    142         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
    143         FragmentTestUtil.waitForExecution(mActivityRule);
    144 
    145         fm.beginTransaction()
    146                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    147                 .hide(fragment)
    148                 .addToBackStack(null)
    149                 .commit();
    150         FragmentTestUtil.waitForExecution(mActivityRule);
    151 
    152         assertExitPopEnter(fragment);
    153     }
    154 
    155     // Ensure that attaching and popping a Fragment uses the enter and popExit animators
    156     @Test
    157     public void attachAnimators() throws Throwable {
    158         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    159 
    160         // One fragment with a view
    161         final AnimatorFragment fragment = new AnimatorFragment();
    162         fm.beginTransaction().add(R.id.fragmentContainer, fragment).detach(fragment).commit();
    163         FragmentTestUtil.waitForExecution(mActivityRule);
    164 
    165         fm.beginTransaction()
    166                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    167                 .attach(fragment)
    168                 .addToBackStack(null)
    169                 .commit();
    170         FragmentTestUtil.waitForExecution(mActivityRule);
    171 
    172         assertEnterPopExit(fragment);
    173     }
    174 
    175     // Ensure that detaching and popping a Fragment uses the exit and popEnter animators
    176     @Test
    177     public void detachAnimators() throws Throwable {
    178         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    179 
    180         // One fragment with a view
    181         final AnimatorFragment fragment = new AnimatorFragment();
    182         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
    183         FragmentTestUtil.waitForExecution(mActivityRule);
    184 
    185         fm.beginTransaction()
    186                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    187                 .detach(fragment)
    188                 .addToBackStack(null)
    189                 .commit();
    190         FragmentTestUtil.waitForExecution(mActivityRule);
    191 
    192         assertExitPopEnter(fragment);
    193     }
    194 
    195     // Replace should exit the existing fragments and enter the added fragment, then
    196     // popping should popExit the removed fragment and popEnter the added fragments
    197     @Test
    198     public void replaceAnimators() throws Throwable {
    199         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    200 
    201         // One fragment with a view
    202         final AnimatorFragment fragment1 = new AnimatorFragment();
    203         final AnimatorFragment fragment2 = new AnimatorFragment();
    204         fm.beginTransaction()
    205                 .add(R.id.fragmentContainer, fragment1, "1")
    206                 .add(R.id.fragmentContainer, fragment2, "2")
    207                 .commit();
    208         FragmentTestUtil.waitForExecution(mActivityRule);
    209 
    210         final AnimatorFragment fragment3 = new AnimatorFragment();
    211         fm.beginTransaction()
    212                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    213                 .replace(R.id.fragmentContainer, fragment3)
    214                 .addToBackStack(null)
    215                 .commit();
    216         FragmentTestUtil.waitForExecution(mActivityRule);
    217 
    218         assertFragmentAnimation(fragment1, 1, false, EXIT);
    219         assertFragmentAnimation(fragment2, 1, false, EXIT);
    220         assertFragmentAnimation(fragment3, 1, true, ENTER);
    221 
    222         fm.popBackStack();
    223         FragmentTestUtil.waitForExecution(mActivityRule);
    224 
    225         assertFragmentAnimation(fragment3, 2, false, POP_EXIT);
    226         final AnimatorFragment replacement1 = (AnimatorFragment) fm.findFragmentByTag("1");
    227         final AnimatorFragment replacement2 = (AnimatorFragment) fm.findFragmentByTag("1");
    228         int expectedAnimations = replacement1 == fragment1 ? 2 : 1;
    229         assertFragmentAnimation(replacement1, expectedAnimations, true, POP_ENTER);
    230         assertFragmentAnimation(replacement2, expectedAnimations, true, POP_ENTER);
    231     }
    232 
    233     // Ensure that adding and popping a Fragment uses the enter and popExit animators,
    234     // but the animators are delayed when an entering Fragment is postponed.
    235     @Test
    236     public void postponedAddAnimators() throws Throwable {
    237         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    238 
    239         final AnimatorFragment fragment = new AnimatorFragment();
    240         fragment.postponeEnterTransition();
    241         fm.beginTransaction()
    242                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    243                 .add(R.id.fragmentContainer, fragment)
    244                 .addToBackStack(null)
    245                 .setReorderingAllowed(true)
    246                 .commit();
    247         FragmentTestUtil.waitForExecution(mActivityRule);
    248 
    249         assertPostponed(fragment, 0);
    250         fragment.startPostponedEnterTransition();
    251 
    252         FragmentTestUtil.waitForExecution(mActivityRule);
    253         assertEnterPopExit(fragment);
    254     }
    255 
    256     // Ensure that removing and popping a Fragment uses the exit and popEnter animators,
    257     // but the animators are delayed when an entering Fragment is postponed.
    258     @Test
    259     public void postponedRemoveAnimators() throws Throwable {
    260         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    261 
    262         final AnimatorFragment fragment = new AnimatorFragment();
    263         fm.beginTransaction().add(R.id.fragmentContainer, fragment, "1").commit();
    264         FragmentTestUtil.waitForExecution(mActivityRule);
    265 
    266         fm.beginTransaction()
    267                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    268                 .remove(fragment)
    269                 .addToBackStack(null)
    270                 .setReorderingAllowed(true)
    271                 .commit();
    272         FragmentTestUtil.waitForExecution(mActivityRule);
    273 
    274         assertExitPostponedPopEnter(fragment);
    275     }
    276 
    277     // Ensure that adding and popping a Fragment is postponed in both directions
    278     // when the fragments have been marked for postponing.
    279     @Test
    280     public void postponedAddRemove() throws Throwable {
    281         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    282 
    283         final AnimatorFragment fragment1 = new AnimatorFragment();
    284         fm.beginTransaction()
    285                 .add(R.id.fragmentContainer, fragment1)
    286                 .addToBackStack(null)
    287                 .setReorderingAllowed(true)
    288                 .commit();
    289         FragmentTestUtil.waitForExecution(mActivityRule);
    290 
    291         final AnimatorFragment fragment2 = new AnimatorFragment();
    292         fragment2.postponeEnterTransition();
    293 
    294         fm.beginTransaction()
    295                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    296                 .replace(R.id.fragmentContainer, fragment2)
    297                 .addToBackStack(null)
    298                 .setReorderingAllowed(true)
    299                 .commit();
    300 
    301         FragmentTestUtil.waitForExecution(mActivityRule);
    302 
    303         assertPostponed(fragment2, 0);
    304         assertNotNull(fragment1.getView());
    305         assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
    306         assertEquals(1f, fragment1.getView().getAlpha(), 0f);
    307         assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
    308 
    309         fragment2.startPostponedEnterTransition();
    310         FragmentTestUtil.waitForExecution(mActivityRule);
    311 
    312         assertExitPostponedPopEnter(fragment1);
    313     }
    314 
    315     // Popping a postponed transaction should result in no animators
    316     @Test
    317     public void popPostponed() throws Throwable {
    318         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    319 
    320         final AnimatorFragment fragment1 = new AnimatorFragment();
    321         fm.beginTransaction()
    322                 .add(R.id.fragmentContainer, fragment1)
    323                 .setReorderingAllowed(true)
    324                 .commit();
    325         FragmentTestUtil.waitForExecution(mActivityRule);
    326         assertEquals(0, fragment1.numAnimators);
    327 
    328         final AnimatorFragment fragment2 = new AnimatorFragment();
    329         fragment2.postponeEnterTransition();
    330 
    331         fm.beginTransaction()
    332                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
    333                 .replace(R.id.fragmentContainer, fragment2)
    334                 .addToBackStack(null)
    335                 .setReorderingAllowed(true)
    336                 .commit();
    337 
    338         FragmentTestUtil.waitForExecution(mActivityRule);
    339 
    340         assertPostponed(fragment2, 0);
    341 
    342         // Now pop the postponed transaction
    343         FragmentTestUtil.popBackStackImmediate(mActivityRule);
    344 
    345         assertNotNull(fragment1.getView());
    346         assertEquals(View.VISIBLE, fragment1.getView().getVisibility());
    347         assertEquals(1f, fragment1.getView().getAlpha(), 0f);
    348         assertTrue(ViewCompat.isAttachedToWindow(fragment1.getView()));
    349         assertTrue(fragment1.isAdded());
    350 
    351         assertNull(fragment2.getView());
    352         assertFalse(fragment2.isAdded());
    353 
    354         assertEquals(0, fragment1.numAnimators);
    355         assertEquals(0, fragment2.numAnimators);
    356         assertNull(fragment1.animation);
    357         assertNull(fragment2.animation);
    358     }
    359 
    360     // Make sure that if the state was saved while a Fragment was animating that its
    361     // state is proper after restoring.
    362     @Test
    363     public void saveWhileAnimatingAway() throws Throwable {
    364         final FragmentController fc1 = FragmentTestUtil.createController(mActivityRule);
    365         FragmentTestUtil.resume(mActivityRule, fc1, null);
    366 
    367         final FragmentManager fm1 = fc1.getSupportFragmentManager();
    368 
    369         StrictViewFragment fragment1 = new StrictViewFragment();
    370         fragment1.setLayoutId(R.layout.scene1);
    371         fm1.beginTransaction()
    372                 .add(R.id.fragmentContainer, fragment1, "1")
    373                 .commit();
    374         FragmentTestUtil.waitForExecution(mActivityRule);
    375 
    376         StrictViewFragment fragment2 = new StrictViewFragment();
    377 
    378         fm1.beginTransaction()
    379                 .setCustomAnimations(0, 0, 0, R.anim.long_fade_out)
    380                 .replace(R.id.fragmentContainer, fragment2, "2")
    381                 .addToBackStack(null)
    382                 .commit();
    383         mInstrumentation.runOnMainSync(new Runnable() {
    384             @Override
    385             public void run() {
    386                 fm1.executePendingTransactions();
    387             }
    388         });
    389         FragmentTestUtil.waitForExecution(mActivityRule);
    390 
    391         fm1.popBackStack();
    392 
    393         mInstrumentation.runOnMainSync(new Runnable() {
    394             @Override
    395             public void run() {
    396                 fm1.executePendingTransactions();
    397             }
    398         });
    399         FragmentTestUtil.waitForExecution(mActivityRule);
    400         // Now fragment2 should be animating away
    401         assertFalse(fragment2.isAdded());
    402         assertEquals(fragment2, fm1.findFragmentByTag("2")); // still exists because it is animating
    403 
    404         Pair<Parcelable, FragmentManagerNonConfig> state =
    405                 FragmentTestUtil.destroy(mActivityRule, fc1);
    406 
    407         final FragmentController fc2 = FragmentTestUtil.createController(mActivityRule);
    408         FragmentTestUtil.resume(mActivityRule, fc2, state);
    409 
    410         final FragmentManager fm2 = fc2.getSupportFragmentManager();
    411         Fragment fragment2restored = fm2.findFragmentByTag("2");
    412         assertNull(fragment2restored);
    413 
    414         Fragment fragment1restored = fm2.findFragmentByTag("1");
    415         assertNotNull(fragment1restored);
    416         assertNotNull(fragment1restored.getView());
    417     }
    418 
    419     // When an animation is running on a Fragment's View, the view shouldn't be
    420     // prevented from being removed. There's no way to directly test this, so we have to
    421     // test to see if the animation is still running.
    422     @Test
    423     public void clearAnimations() throws Throwable {
    424         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    425 
    426         final StrictViewFragment fragment1 = new StrictViewFragment();
    427         fm.beginTransaction()
    428                 .add(R.id.fragmentContainer, fragment1)
    429                 .setReorderingAllowed(true)
    430                 .commit();
    431         FragmentTestUtil.waitForExecution(mActivityRule);
    432 
    433         final View fragmentView = fragment1.getView();
    434 
    435         final TranslateAnimation xAnimation = new TranslateAnimation(0, 1000, 0, 0);
    436         mActivityRule.runOnUiThread(new Runnable() {
    437             @Override
    438             public void run() {
    439                 fragmentView.startAnimation(xAnimation);
    440             }
    441         });
    442 
    443         FragmentTestUtil.waitForExecution(mActivityRule);
    444         FragmentTestUtil.popBackStackImmediate(mActivityRule);
    445         mActivityRule.runOnUiThread(new Runnable() {
    446             @Override
    447             public void run() {
    448                 assertNull(fragmentView.getAnimation());
    449             }
    450         });
    451     }
    452 
    453     // When a view is animated out, is parent should be null after the animation completes
    454     @Test
    455     public void parentNullAfterAnimation() throws Throwable {
    456         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    457 
    458         final EndAnimationListenerFragment fragment1 = new EndAnimationListenerFragment();
    459         fm.beginTransaction()
    460                 .add(R.id.fragmentContainer, fragment1)
    461                 .commit();
    462         FragmentTestUtil.waitForExecution(mActivityRule);
    463 
    464         final EndAnimationListenerFragment fragment2 = new EndAnimationListenerFragment();
    465 
    466         fm.beginTransaction()
    467                 .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out,
    468                         android.R.anim.fade_in, android.R.anim.fade_out)
    469                 .replace(R.id.fragmentContainer, fragment2)
    470                 .addToBackStack(null)
    471                 .commit();
    472 
    473         FragmentTestUtil.waitForExecution(mActivityRule);
    474 
    475         assertTrue(fragment1.exitLatch.await(1, TimeUnit.SECONDS));
    476         assertTrue(fragment2.enterLatch.await(1, TimeUnit.SECONDS));
    477 
    478         mActivityRule.runOnUiThread(new Runnable() {
    479             @Override
    480             public void run() {
    481                 assertNotNull(fragment1.view);
    482                 assertNotNull(fragment2.view);
    483                 assertNull(fragment1.view.getParent());
    484             }
    485         });
    486 
    487         // Now pop the transaction
    488         FragmentTestUtil.popBackStackImmediate(mActivityRule);
    489 
    490         assertTrue(fragment2.exitLatch.await(1, TimeUnit.SECONDS));
    491         assertTrue(fragment1.enterLatch.await(1, TimeUnit.SECONDS));
    492 
    493         mActivityRule.runOnUiThread(new Runnable() {
    494             @Override
    495             public void run() {
    496                 assertNull(fragment2.view.getParent());
    497             }
    498         });
    499     }
    500 
    501     private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
    502         assertFragmentAnimation(fragment, 1, true, ENTER);
    503 
    504         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    505         fm.popBackStack();
    506         FragmentTestUtil.waitForExecution(mActivityRule);
    507 
    508         assertFragmentAnimation(fragment, 2, false, POP_EXIT);
    509     }
    510 
    511     private void assertExitPopEnter(AnimatorFragment fragment) throws Throwable {
    512         assertFragmentAnimation(fragment, 1, false, EXIT);
    513 
    514         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
    515         fm.popBackStack();
    516         FragmentTestUtil.waitForExecution(mActivityRule);
    517 
    518         AnimatorFragment replacement = (AnimatorFragment) fm.findFragmentByTag("1");
    519 
    520         boolean isSameFragment = replacement == fragment;
    521         int expectedAnimators = isSameFragment ? 2 : 1;
    522         assertFragmentAnimation(replacement, expectedAnimators, true, POP_ENTER);
    523     }
    524 
    525     private void assertExitPostponedPopEnter(AnimatorFragment fragment) throws Throwable {
    526         assertFragmentAnimation(fragment, 1, false, EXIT);
    527 
    528         fragment.postponeEnterTransition();
    529         FragmentTestUtil.popBackStackImmediate(mActivityRule);
    530 
    531         assertPostponed(fragment, 1);
    532 
    533         fragment.startPostponedEnterTransition();
    534         FragmentTestUtil.waitForExecution(mActivityRule);
    535         assertFragmentAnimation(fragment, 2, true, POP_ENTER);
    536     }
    537 
    538     private void assertFragmentAnimation(AnimatorFragment fragment, int numAnimators,
    539             boolean isEnter, int animatorResourceId) throws InterruptedException {
    540         assertEquals(numAnimators, fragment.numAnimators);
    541         assertEquals(isEnter, fragment.enter);
    542         assertEquals(animatorResourceId, fragment.resourceId);
    543         assertNotNull(fragment.animation);
    544         assertTrue(FragmentTestUtil.waitForAnimationEnd(1000, fragment.animation));
    545         assertTrue(fragment.animation.hasStarted());
    546     }
    547 
    548     private void assertPostponed(AnimatorFragment fragment, int expectedAnimators)
    549             throws InterruptedException {
    550         assertTrue(fragment.mOnCreateViewCalled);
    551         assertEquals(View.VISIBLE, fragment.getView().getVisibility());
    552         assertEquals(0f, fragment.getView().getAlpha(), 0f);
    553         assertEquals(expectedAnimators, fragment.numAnimators);
    554     }
    555 
    556     public static class AnimatorFragment extends StrictViewFragment {
    557         public int numAnimators;
    558         public Animation animation;
    559         public boolean enter;
    560         public int resourceId;
    561 
    562         @Override
    563         public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    564             if (nextAnim == 0) {
    565                 return null;
    566             }
    567             this.numAnimators++;
    568             this.animation = new TranslateAnimation(-10, 0, 0, 0);
    569             this.animation.setDuration(1);
    570             this.resourceId = nextAnim;
    571             this.enter = enter;
    572             return this.animation;
    573         }
    574     }
    575 
    576     public static class EndAnimationListenerFragment extends StrictViewFragment {
    577         public View view;
    578         public final CountDownLatch enterLatch = new CountDownLatch(1);
    579         public final CountDownLatch exitLatch = new CountDownLatch(1);
    580 
    581         @Override
    582         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    583                 Bundle savedInstanceState) {
    584             if (view != null) {
    585                 return view;
    586             }
    587             view = super.onCreateView(inflater, container, savedInstanceState);
    588             return view;
    589         }
    590 
    591         @Override
    592         public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) {
    593             if (nextAnim == 0) {
    594                 return null;
    595             }
    596             Animation anim = AnimationUtils.loadAnimation(getActivity(), nextAnim);
    597             if (anim != null) {
    598                 anim.setAnimationListener(new Animation.AnimationListener() {
    599                     @Override
    600                     public void onAnimationStart(Animation animation) {
    601                     }
    602 
    603                     @Override
    604                     public void onAnimationEnd(Animation animation) {
    605                         if (enter) {
    606                             enterLatch.countDown();
    607                         } else {
    608                             exitLatch.countDown();
    609                         }
    610                     }
    611 
    612                     @Override
    613                     public void onAnimationRepeat(Animation animation) {
    614 
    615                     }
    616                 });
    617             }
    618             return anim;
    619         }
    620     }
    621 }
    622