blob: 698b5638795aab7eeef52cbacdde6f711204c98f [file] [log] [blame]
Chet Haased82c8ac2013-08-26 14:20:16 -07001/*
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
17package android.transition;
18
19import android.animation.TimeInterpolator;
George Mountd6107a32014-03-10 16:51:16 -070020import android.graphics.Rect;
Chet Haased82c8ac2013-08-26 14:20:16 -070021import android.util.AndroidRuntimeException;
22import android.view.View;
23import android.view.ViewGroup;
24
25import java.util.ArrayList;
26
27/**
28 * A TransitionSet is a parent of child transitions (including other
29 * TransitionSets). Using TransitionSets enables more complex
30 * choreography of transitions, where some sets play {@link #ORDERING_TOGETHER} and
31 * others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
32 * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
33 * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
Chet Haaseaafc91c2013-10-06 12:10:12 -070034 *
35 * <p>A TransitionSet can be described in a resource file by using the
36 * tag <code>transitionSet</code>, along with the standard
37 * attributes of {@link android.R.styleable#TransitionSet} and
38 * {@link android.R.styleable#Transition}. Child transitions of the
39 * TransitionSet object can be loaded by adding those child tags inside the
40 * enclosing <code>transitionSet</code> tag. For example, the following xml
41 * describes a TransitionSet that plays a Fade and then a ChangeBounds
42 * transition on the affected view targets:</p>
43 * <pre>
44 * &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
45 * android:ordering="sequential"&gt;
46 * &lt;fade/&gt;
47 * &lt;changeBounds/&gt;
48 * &lt;/transitionSet&gt;
49 * </pre>
Chet Haased82c8ac2013-08-26 14:20:16 -070050 */
51public class TransitionSet extends Transition {
52
53 ArrayList<Transition> mTransitions = new ArrayList<Transition>();
54 private boolean mPlayTogether = true;
55 int mCurrentListeners;
56 boolean mStarted = false;
57
58 /**
59 * A flag used to indicate that the child transitions of this set
60 * should all start at the same time.
61 */
62 public static final int ORDERING_TOGETHER = 0;
63 /**
64 * A flag used to indicate that the child transitions of this set should
65 * play in sequence; when one child transition ends, the next child
66 * transition begins. Note that a transition does not end until all
67 * instances of it (which are playing on all applicable targets of the
68 * transition) end.
69 */
70 public static final int ORDERING_SEQUENTIAL = 1;
71
72 /**
73 * Constructs an empty transition set. Add child transitions to the
74 * set by calling {@link #addTransition(Transition)} )}. By default,
75 * child transitions will play {@link #ORDERING_TOGETHER together}.
76 */
77 public TransitionSet() {
78 }
79
80 /**
81 * Sets the play order of this set's child transitions.
82 *
83 * @param ordering {@link #ORDERING_TOGETHER} to play this set's child
84 * transitions together, {@link #ORDERING_SEQUENTIAL} to play the child
85 * transitions in sequence.
86 * @return This transitionSet object.
87 */
88 public TransitionSet setOrdering(int ordering) {
89 switch (ordering) {
90 case ORDERING_SEQUENTIAL:
91 mPlayTogether = false;
92 break;
93 case ORDERING_TOGETHER:
94 mPlayTogether = true;
95 break;
96 default:
97 throw new AndroidRuntimeException("Invalid parameter for TransitionSet " +
98 "ordering: " + ordering);
99 }
100 return this;
101 }
102
103 /**
104 * Returns the ordering of this TransitionSet. By default, the value is
105 * {@link #ORDERING_TOGETHER}.
106 *
107 * @return {@link #ORDERING_TOGETHER} if child transitions will play at the same
108 * time, {@link #ORDERING_SEQUENTIAL} if they will play in sequence.
109 *
110 * @see #setOrdering(int)
111 */
112 public int getOrdering() {
113 return mPlayTogether ? ORDERING_TOGETHER : ORDERING_SEQUENTIAL;
114 }
115
116 /**
117 * Adds child transition to this set. The order in which this child transition
118 * is added relative to other child transitions that are added, in addition to
119 * the {@link #getOrdering() ordering} property, determines the
120 * order in which the transitions are started.
121 *
122 * <p>If this transitionSet has a {@link #getDuration() duration} set on it, the
123 * child transition will inherit that duration. Transitions are assumed to have
124 * a maximum of one transitionSet parent.</p>
125 *
126 * @param transition A non-null child transition to be added to this set.
127 * @return This transitionSet object.
128 */
129 public TransitionSet addTransition(Transition transition) {
130 if (transition != null) {
131 mTransitions.add(transition);
132 transition.mParent = this;
133 if (mDuration >= 0) {
134 transition.setDuration(mDuration);
135 }
136 }
137 return this;
138 }
139
140 /**
141 * Setting a non-negative duration on a TransitionSet causes all of the child
142 * transitions (current and future) to inherit this duration.
143 *
144 * @param duration The length of the animation, in milliseconds.
145 * @return This transitionSet object.
146 */
147 @Override
148 public TransitionSet setDuration(long duration) {
149 super.setDuration(duration);
150 if (mDuration >= 0) {
151 int numTransitions = mTransitions.size();
152 for (int i = 0; i < numTransitions; ++i) {
153 mTransitions.get(i).setDuration(duration);
154 }
155 }
156 return this;
157 }
158
159 @Override
160 public TransitionSet setStartDelay(long startDelay) {
161 return (TransitionSet) super.setStartDelay(startDelay);
162 }
163
164 @Override
165 public TransitionSet setInterpolator(TimeInterpolator interpolator) {
166 return (TransitionSet) super.setInterpolator(interpolator);
167 }
168
169 @Override
170 public TransitionSet addTarget(View target) {
171 return (TransitionSet) super.addTarget(target);
172 }
173
174 @Override
Chet Haaseff58f922013-09-11 13:08:18 -0700175 public TransitionSet addTarget(int targetId) {
176 return (TransitionSet) super.addTarget(targetId);
Chet Haased82c8ac2013-08-26 14:20:16 -0700177 }
178
179 @Override
180 public TransitionSet addListener(TransitionListener listener) {
181 return (TransitionSet) super.addListener(listener);
182 }
183
184 @Override
Chet Haaseff58f922013-09-11 13:08:18 -0700185 public TransitionSet removeTarget(int targetId) {
186 return (TransitionSet) super.removeTarget(targetId);
Chet Haased82c8ac2013-08-26 14:20:16 -0700187 }
188
189 @Override
190 public TransitionSet removeTarget(View target) {
191 return (TransitionSet) super.removeTarget(target);
192 }
193
194 @Override
195 public TransitionSet removeListener(TransitionListener listener) {
196 return (TransitionSet) super.removeListener(listener);
197 }
198
199 /**
200 * Removes the specified child transition from this set.
201 *
202 * @param transition The transition to be removed.
203 * @return This transitionSet object.
204 */
205 public TransitionSet removeTransition(Transition transition) {
206 mTransitions.remove(transition);
207 transition.mParent = null;
208 return this;
209 }
210
211 /**
212 * Sets up listeners for each of the child transitions. This is used to
213 * determine when this transition set is finished (all child transitions
214 * must finish first).
215 */
216 private void setupStartEndListeners() {
217 TransitionSetListener listener = new TransitionSetListener(this);
218 for (Transition childTransition : mTransitions) {
219 childTransition.addListener(listener);
220 }
221 mCurrentListeners = mTransitions.size();
222 }
223
224 /**
225 * This listener is used to detect when all child transitions are done, at
226 * which point this transition set is also done.
227 */
228 static class TransitionSetListener extends TransitionListenerAdapter {
229 TransitionSet mTransitionSet;
230 TransitionSetListener(TransitionSet transitionSet) {
231 mTransitionSet = transitionSet;
232 }
233 @Override
234 public void onTransitionStart(Transition transition) {
235 if (!mTransitionSet.mStarted) {
236 mTransitionSet.start();
237 mTransitionSet.mStarted = true;
238 }
239 }
240
241 @Override
242 public void onTransitionEnd(Transition transition) {
243 --mTransitionSet.mCurrentListeners;
244 if (mTransitionSet.mCurrentListeners == 0) {
245 // All child trans
246 mTransitionSet.mStarted = false;
247 mTransitionSet.end();
248 }
249 transition.removeListener(this);
250 }
251 }
252
253 /**
254 * @hide
255 */
256 @Override
257 protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
258 TransitionValuesMaps endValues) {
George Mount8c0ceb32014-04-15 09:01:32 -0700259 startValues = removeExcludes(startValues);
260 endValues = removeExcludes(endValues);
Chet Haased82c8ac2013-08-26 14:20:16 -0700261 for (Transition childTransition : mTransitions) {
262 childTransition.createAnimators(sceneRoot, startValues, endValues);
263 }
264 }
265
George Mount8c0ceb32014-04-15 09:01:32 -0700266 private TransitionValuesMaps removeExcludes(TransitionValuesMaps values) {
267 if (mTargetIds.isEmpty() && mTargetIdExcludes == null && mTargetTypeExcludes == null
268 && mTargets.isEmpty()) {
269 return values;
270 }
271 TransitionValuesMaps included = new TransitionValuesMaps();
272 int numValues = values.viewValues.size();
273 for (int i = 0; i < numValues; i++) {
274 View view = values.viewValues.keyAt(i);
George Mount30da61d2014-05-09 13:17:52 -0700275 if (isValidTarget(view)) {
276 addViewValues(included, view, values.viewValues.valueAt(i));
George Mount8c0ceb32014-04-15 09:01:32 -0700277 }
278 }
279 return included;
280 }
281
Chet Haased82c8ac2013-08-26 14:20:16 -0700282 /**
283 * @hide
284 */
285 @Override
286 protected void runAnimators() {
287 setupStartEndListeners();
288 if (!mPlayTogether) {
289 // Setup sequence with listeners
290 // TODO: Need to add listeners in such a way that we can remove them later if canceled
291 for (int i = 1; i < mTransitions.size(); ++i) {
292 Transition previousTransition = mTransitions.get(i - 1);
293 final Transition nextTransition = mTransitions.get(i);
294 previousTransition.addListener(new TransitionListenerAdapter() {
295 @Override
296 public void onTransitionEnd(Transition transition) {
297 nextTransition.runAnimators();
298 transition.removeListener(this);
299 }
300 });
301 }
302 Transition firstTransition = mTransitions.get(0);
303 if (firstTransition != null) {
304 firstTransition.runAnimators();
305 }
306 } else {
307 for (Transition childTransition : mTransitions) {
308 childTransition.runAnimators();
309 }
310 }
311 }
312
313 @Override
314 public void captureStartValues(TransitionValues transitionValues) {
George Mount30da61d2014-05-09 13:17:52 -0700315 if (isValidTarget(transitionValues.view)) {
Chet Haaseff58f922013-09-11 13:08:18 -0700316 for (Transition childTransition : mTransitions) {
George Mount30da61d2014-05-09 13:17:52 -0700317 if (childTransition.isValidTarget(transitionValues.view)) {
Chet Haaseff58f922013-09-11 13:08:18 -0700318 childTransition.captureStartValues(transitionValues);
319 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700320 }
321 }
322 }
323
324 @Override
325 public void captureEndValues(TransitionValues transitionValues) {
George Mount30da61d2014-05-09 13:17:52 -0700326 if (isValidTarget(transitionValues.view)) {
Chet Haaseff58f922013-09-11 13:08:18 -0700327 for (Transition childTransition : mTransitions) {
George Mount30da61d2014-05-09 13:17:52 -0700328 if (childTransition.isValidTarget(transitionValues.view)) {
Chet Haaseff58f922013-09-11 13:08:18 -0700329 childTransition.captureEndValues(transitionValues);
330 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700331 }
332 }
333 }
334
George Mountd6107a32014-03-10 16:51:16 -0700335 @Override
336 void capturePropagationValues(TransitionValues transitionValues) {
337 super.capturePropagationValues(transitionValues);
338 int numTransitions = mTransitions.size();
339 for (int i = 0; i < numTransitions; ++i) {
340 mTransitions.get(i).capturePropagationValues(transitionValues);
341 }
342 }
343
Chet Haased82c8ac2013-08-26 14:20:16 -0700344 /** @hide */
345 @Override
George Mountcf68aad2014-03-06 14:17:46 -0800346 public void pause(View sceneRoot) {
347 super.pause(sceneRoot);
Chet Haased82c8ac2013-08-26 14:20:16 -0700348 int numTransitions = mTransitions.size();
349 for (int i = 0; i < numTransitions; ++i) {
George Mountcf68aad2014-03-06 14:17:46 -0800350 mTransitions.get(i).pause(sceneRoot);
Chet Haased82c8ac2013-08-26 14:20:16 -0700351 }
352 }
353
354 /** @hide */
355 @Override
George Mountcf68aad2014-03-06 14:17:46 -0800356 public void resume(View sceneRoot) {
357 super.resume(sceneRoot);
Chet Haased82c8ac2013-08-26 14:20:16 -0700358 int numTransitions = mTransitions.size();
359 for (int i = 0; i < numTransitions; ++i) {
George Mountcf68aad2014-03-06 14:17:46 -0800360 mTransitions.get(i).resume(sceneRoot);
Chet Haased82c8ac2013-08-26 14:20:16 -0700361 }
362 }
363
364 /** @hide */
365 @Override
366 protected void cancel() {
367 super.cancel();
368 int numTransitions = mTransitions.size();
369 for (int i = 0; i < numTransitions; ++i) {
370 mTransitions.get(i).cancel();
371 }
372 }
373
374 @Override
375 TransitionSet setSceneRoot(ViewGroup sceneRoot) {
376 super.setSceneRoot(sceneRoot);
377 int numTransitions = mTransitions.size();
378 for (int i = 0; i < numTransitions; ++i) {
379 mTransitions.get(i).setSceneRoot(sceneRoot);
380 }
381 return (TransitionSet) this;
382 }
383
384 @Override
Chet Haaseb7a7fc92013-09-20 16:33:08 -0700385 void setCanRemoveViews(boolean canRemoveViews) {
386 super.setCanRemoveViews(canRemoveViews);
387 int numTransitions = mTransitions.size();
388 for (int i = 0; i < numTransitions; ++i) {
389 mTransitions.get(i).setCanRemoveViews(canRemoveViews);
390 }
391 }
392
393 @Override
George Mountd6107a32014-03-10 16:51:16 -0700394 public void setPropagation(TransitionPropagation propagation) {
395 super.setPropagation(propagation);
396 int numTransitions = mTransitions.size();
397 for (int i = 0; i < numTransitions; ++i) {
398 mTransitions.get(i).setPropagation(propagation);
399 }
400 }
401
402 @Override
403 public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
404 super.setEpicenterCallback(epicenterCallback);
405 int numTransitions = mTransitions.size();
406 for (int i = 0; i < numTransitions; ++i) {
407 mTransitions.get(i).setEpicenterCallback(epicenterCallback);
408 }
409 }
410
411 @Override
Chet Haased82c8ac2013-08-26 14:20:16 -0700412 String toString(String indent) {
413 String result = super.toString(indent);
414 for (int i = 0; i < mTransitions.size(); ++i) {
415 result += "\n" + mTransitions.get(i).toString(indent + " ");
416 }
417 return result;
418 }
419
420 @Override
421 public TransitionSet clone() {
422 TransitionSet clone = (TransitionSet) super.clone();
423 clone.mTransitions = new ArrayList<Transition>();
424 int numTransitions = mTransitions.size();
425 for (int i = 0; i < numTransitions; ++i) {
Chet Haasec46181a2013-09-16 13:56:21 -0700426 clone.addTransition((Transition) mTransitions.get(i).clone());
Chet Haased82c8ac2013-08-26 14:20:16 -0700427 }
428 return clone;
429 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700430}