Home | History | Annotate | Download | only in transition
      1 /*
      2  * Copyright (C) 2013 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 
     17 package android.transition;
     18 
     19 import android.animation.TimeInterpolator;
     20 import android.util.AndroidRuntimeException;
     21 import android.view.View;
     22 import android.view.ViewGroup;
     23 
     24 import java.util.ArrayList;
     25 
     26 /**
     27  * A TransitionSet is a parent of child transitions (including other
     28  * TransitionSets). Using TransitionSets enables more complex
     29  * choreography of transitions, where some sets play {@link #ORDERING_TOGETHER} and
     30  * others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
     31  * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
     32  * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
     33  *
     34  * <p>A TransitionSet can be described in a resource file by using the
     35  * tag <code>transitionSet</code>, along with the standard
     36  * attributes of {@link android.R.styleable#TransitionSet} and
     37  * {@link android.R.styleable#Transition}. Child transitions of the
     38  * TransitionSet object can be loaded by adding those child tags inside the
     39  * enclosing <code>transitionSet</code> tag. For example, the following xml
     40  * describes a TransitionSet that plays a Fade and then a ChangeBounds
     41  * transition on the affected view targets:</p>
     42  * <pre>
     43  *     &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
     44  *             android:ordering="sequential"&gt;
     45  *         &lt;fade/&gt;
     46  *         &lt;changeBounds/&gt;
     47  *     &lt;/transitionSet&gt;
     48  * </pre>
     49  */
     50 public class TransitionSet extends Transition {
     51 
     52     ArrayList<Transition> mTransitions = new ArrayList<Transition>();
     53     private boolean mPlayTogether = true;
     54     int mCurrentListeners;
     55     boolean mStarted = false;
     56 
     57     /**
     58      * A flag used to indicate that the child transitions of this set
     59      * should all start at the same time.
     60      */
     61     public static final int ORDERING_TOGETHER = 0;
     62     /**
     63      * A flag used to indicate that the child transitions of this set should
     64      * play in sequence; when one child transition ends, the next child
     65      * transition begins. Note that a transition does not end until all
     66      * instances of it (which are playing on all applicable targets of the
     67      * transition) end.
     68      */
     69     public static final int ORDERING_SEQUENTIAL = 1;
     70 
     71     /**
     72      * Constructs an empty transition set. Add child transitions to the
     73      * set by calling {@link #addTransition(Transition)} )}. By default,
     74      * child transitions will play {@link #ORDERING_TOGETHER together}.
     75      */
     76     public TransitionSet() {
     77     }
     78 
     79     /**
     80      * Sets the play order of this set's child transitions.
     81      *
     82      * @param ordering {@link #ORDERING_TOGETHER} to play this set's child
     83      * transitions together, {@link #ORDERING_SEQUENTIAL} to play the child
     84      * transitions in sequence.
     85      * @return This transitionSet object.
     86      */
     87     public TransitionSet setOrdering(int ordering) {
     88         switch (ordering) {
     89             case ORDERING_SEQUENTIAL:
     90                 mPlayTogether = false;
     91                 break;
     92             case ORDERING_TOGETHER:
     93                 mPlayTogether = true;
     94                 break;
     95             default:
     96                 throw new AndroidRuntimeException("Invalid parameter for TransitionSet " +
     97                         "ordering: " + ordering);
     98         }
     99         return this;
    100     }
    101 
    102     /**
    103      * Returns the ordering of this TransitionSet. By default, the value is
    104      * {@link #ORDERING_TOGETHER}.
    105      *
    106      * @return {@link #ORDERING_TOGETHER} if child transitions will play at the same
    107      * time, {@link #ORDERING_SEQUENTIAL} if they will play in sequence.
    108      *
    109      * @see #setOrdering(int)
    110      */
    111     public int getOrdering() {
    112         return mPlayTogether ? ORDERING_TOGETHER : ORDERING_SEQUENTIAL;
    113     }
    114 
    115     /**
    116      * Adds child transition to this set. The order in which this child transition
    117      * is added relative to other child transitions that are added, in addition to
    118      * the {@link #getOrdering() ordering} property, determines the
    119      * order in which the transitions are started.
    120      *
    121      * <p>If this transitionSet has a {@link #getDuration() duration} set on it, the
    122      * child transition will inherit that duration. Transitions are assumed to have
    123      * a maximum of one transitionSet parent.</p>
    124      *
    125      * @param transition A non-null child transition to be added to this set.
    126      * @return This transitionSet object.
    127      */
    128     public TransitionSet addTransition(Transition transition) {
    129         if (transition != null) {
    130             mTransitions.add(transition);
    131             transition.mParent = this;
    132             if (mDuration >= 0) {
    133                 transition.setDuration(mDuration);
    134             }
    135         }
    136         return this;
    137     }
    138 
    139     /**
    140      * Setting a non-negative duration on a TransitionSet causes all of the child
    141      * transitions (current and future) to inherit this duration.
    142      *
    143      * @param duration The length of the animation, in milliseconds.
    144      * @return This transitionSet object.
    145      */
    146     @Override
    147     public TransitionSet setDuration(long duration) {
    148         super.setDuration(duration);
    149         if (mDuration >= 0) {
    150             int numTransitions = mTransitions.size();
    151             for (int i = 0; i < numTransitions; ++i) {
    152                 mTransitions.get(i).setDuration(duration);
    153             }
    154         }
    155         return this;
    156     }
    157 
    158     @Override
    159     public TransitionSet setStartDelay(long startDelay) {
    160         return (TransitionSet) super.setStartDelay(startDelay);
    161     }
    162 
    163     @Override
    164     public TransitionSet setInterpolator(TimeInterpolator interpolator) {
    165         return (TransitionSet) super.setInterpolator(interpolator);
    166     }
    167 
    168     @Override
    169     public TransitionSet addTarget(View target) {
    170         return (TransitionSet) super.addTarget(target);
    171     }
    172 
    173     @Override
    174     public TransitionSet addTarget(int targetId) {
    175         return (TransitionSet) super.addTarget(targetId);
    176     }
    177 
    178     @Override
    179     public TransitionSet addListener(TransitionListener listener) {
    180         return (TransitionSet) super.addListener(listener);
    181     }
    182 
    183     @Override
    184     public TransitionSet removeTarget(int targetId) {
    185         return (TransitionSet) super.removeTarget(targetId);
    186     }
    187 
    188     @Override
    189     public TransitionSet removeTarget(View target) {
    190         return (TransitionSet) super.removeTarget(target);
    191     }
    192 
    193     @Override
    194     public TransitionSet removeListener(TransitionListener listener) {
    195         return (TransitionSet) super.removeListener(listener);
    196     }
    197 
    198     /**
    199      * Removes the specified child transition from this set.
    200      *
    201      * @param transition The transition to be removed.
    202      * @return This transitionSet object.
    203      */
    204     public TransitionSet removeTransition(Transition transition) {
    205         mTransitions.remove(transition);
    206         transition.mParent = null;
    207         return this;
    208     }
    209 
    210     /**
    211      * Sets up listeners for each of the child transitions. This is used to
    212      * determine when this transition set is finished (all child transitions
    213      * must finish first).
    214      */
    215     private void setupStartEndListeners() {
    216         TransitionSetListener listener = new TransitionSetListener(this);
    217         for (Transition childTransition : mTransitions) {
    218             childTransition.addListener(listener);
    219         }
    220         mCurrentListeners = mTransitions.size();
    221     }
    222 
    223     /**
    224      * This listener is used to detect when all child transitions are done, at
    225      * which point this transition set is also done.
    226      */
    227     static class TransitionSetListener extends TransitionListenerAdapter {
    228         TransitionSet mTransitionSet;
    229         TransitionSetListener(TransitionSet transitionSet) {
    230             mTransitionSet = transitionSet;
    231         }
    232         @Override
    233         public void onTransitionStart(Transition transition) {
    234             if (!mTransitionSet.mStarted) {
    235                 mTransitionSet.start();
    236                 mTransitionSet.mStarted = true;
    237             }
    238         }
    239 
    240         @Override
    241         public void onTransitionEnd(Transition transition) {
    242             --mTransitionSet.mCurrentListeners;
    243             if (mTransitionSet.mCurrentListeners == 0) {
    244                 // All child trans
    245                 mTransitionSet.mStarted = false;
    246                 mTransitionSet.end();
    247             }
    248             transition.removeListener(this);
    249         }
    250     }
    251 
    252     /**
    253      * @hide
    254      */
    255     @Override
    256     protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
    257             TransitionValuesMaps endValues) {
    258         for (Transition childTransition : mTransitions) {
    259             childTransition.createAnimators(sceneRoot, startValues, endValues);
    260         }
    261     }
    262 
    263     /**
    264      * @hide
    265      */
    266     @Override
    267     protected void runAnimators() {
    268         setupStartEndListeners();
    269         if (!mPlayTogether) {
    270             // Setup sequence with listeners
    271             // TODO: Need to add listeners in such a way that we can remove them later if canceled
    272             for (int i = 1; i < mTransitions.size(); ++i) {
    273                 Transition previousTransition = mTransitions.get(i - 1);
    274                 final Transition nextTransition = mTransitions.get(i);
    275                 previousTransition.addListener(new TransitionListenerAdapter() {
    276                     @Override
    277                     public void onTransitionEnd(Transition transition) {
    278                         nextTransition.runAnimators();
    279                         transition.removeListener(this);
    280                     }
    281                 });
    282             }
    283             Transition firstTransition = mTransitions.get(0);
    284             if (firstTransition != null) {
    285                 firstTransition.runAnimators();
    286             }
    287         } else {
    288             for (Transition childTransition : mTransitions) {
    289                 childTransition.runAnimators();
    290             }
    291         }
    292     }
    293 
    294     @Override
    295     public void captureStartValues(TransitionValues transitionValues) {
    296         int targetId = transitionValues.view.getId();
    297         if (isValidTarget(transitionValues.view, targetId)) {
    298             for (Transition childTransition : mTransitions) {
    299                 if (childTransition.isValidTarget(transitionValues.view, targetId)) {
    300                     childTransition.captureStartValues(transitionValues);
    301                 }
    302             }
    303         }
    304     }
    305 
    306     @Override
    307     public void captureEndValues(TransitionValues transitionValues) {
    308         int targetId = transitionValues.view.getId();
    309         if (isValidTarget(transitionValues.view, targetId)) {
    310             for (Transition childTransition : mTransitions) {
    311                 if (childTransition.isValidTarget(transitionValues.view, targetId)) {
    312                     childTransition.captureEndValues(transitionValues);
    313                 }
    314             }
    315         }
    316     }
    317 
    318     /** @hide */
    319     @Override
    320     public void pause() {
    321         super.pause();
    322         int numTransitions = mTransitions.size();
    323         for (int i = 0; i < numTransitions; ++i) {
    324             mTransitions.get(i).pause();
    325         }
    326     }
    327 
    328     /** @hide */
    329     @Override
    330     public void resume() {
    331         super.resume();
    332         int numTransitions = mTransitions.size();
    333         for (int i = 0; i < numTransitions; ++i) {
    334             mTransitions.get(i).resume();
    335         }
    336     }
    337 
    338     /** @hide */
    339     @Override
    340     protected void cancel() {
    341         super.cancel();
    342         int numTransitions = mTransitions.size();
    343         for (int i = 0; i < numTransitions; ++i) {
    344             mTransitions.get(i).cancel();
    345         }
    346     }
    347 
    348     @Override
    349     TransitionSet setSceneRoot(ViewGroup sceneRoot) {
    350         super.setSceneRoot(sceneRoot);
    351         int numTransitions = mTransitions.size();
    352         for (int i = 0; i < numTransitions; ++i) {
    353             mTransitions.get(i).setSceneRoot(sceneRoot);
    354         }
    355         return (TransitionSet) this;
    356     }
    357 
    358     @Override
    359     void setCanRemoveViews(boolean canRemoveViews) {
    360         super.setCanRemoveViews(canRemoveViews);
    361         int numTransitions = mTransitions.size();
    362         for (int i = 0; i < numTransitions; ++i) {
    363             mTransitions.get(i).setCanRemoveViews(canRemoveViews);
    364         }
    365     }
    366 
    367     @Override
    368     String toString(String indent) {
    369         String result = super.toString(indent);
    370         for (int i = 0; i < mTransitions.size(); ++i) {
    371             result += "\n" + mTransitions.get(i).toString(indent + "  ");
    372         }
    373         return result;
    374     }
    375 
    376     @Override
    377     public TransitionSet clone() {
    378         TransitionSet clone = (TransitionSet) super.clone();
    379         clone.mTransitions = new ArrayList<Transition>();
    380         int numTransitions = mTransitions.size();
    381         for (int i = 0; i < numTransitions; ++i) {
    382             clone.addTransition((Transition) mTransitions.get(i).clone());
    383         }
    384         return clone;
    385     }
    386 
    387 }
    388