• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
TransitionSet()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      */
setOrdering(int ordering)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      */
getOrdering()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      */
addTransition(Transition transition)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
setDuration(long duration)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
setStartDelay(long startDelay)159     public TransitionSet setStartDelay(long startDelay) {
160         return (TransitionSet) super.setStartDelay(startDelay);
161     }
162 
163     @Override
setInterpolator(TimeInterpolator interpolator)164     public TransitionSet setInterpolator(TimeInterpolator interpolator) {
165         return (TransitionSet) super.setInterpolator(interpolator);
166     }
167 
168     @Override
addTarget(View target)169     public TransitionSet addTarget(View target) {
170         return (TransitionSet) super.addTarget(target);
171     }
172 
173     @Override
addTarget(int targetId)174     public TransitionSet addTarget(int targetId) {
175         return (TransitionSet) super.addTarget(targetId);
176     }
177 
178     @Override
addListener(TransitionListener listener)179     public TransitionSet addListener(TransitionListener listener) {
180         return (TransitionSet) super.addListener(listener);
181     }
182 
183     @Override
removeTarget(int targetId)184     public TransitionSet removeTarget(int targetId) {
185         return (TransitionSet) super.removeTarget(targetId);
186     }
187 
188     @Override
removeTarget(View target)189     public TransitionSet removeTarget(View target) {
190         return (TransitionSet) super.removeTarget(target);
191     }
192 
193     @Override
removeListener(TransitionListener listener)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      */
removeTransition(Transition transition)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      */
setupStartEndListeners()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;
TransitionSetListener(TransitionSet transitionSet)229         TransitionSetListener(TransitionSet transitionSet) {
230             mTransitionSet = transitionSet;
231         }
232         @Override
onTransitionStart(Transition transition)233         public void onTransitionStart(Transition transition) {
234             if (!mTransitionSet.mStarted) {
235                 mTransitionSet.start();
236                 mTransitionSet.mStarted = true;
237             }
238         }
239 
240         @Override
onTransitionEnd(Transition transition)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
createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues, TransitionValuesMaps endValues)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
runAnimators()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
captureStartValues(TransitionValues transitionValues)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
captureEndValues(TransitionValues transitionValues)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
pause()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
resume()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
cancel()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
setSceneRoot(ViewGroup sceneRoot)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
setCanRemoveViews(boolean canRemoveViews)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
toString(String indent)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
clone()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