TransitionSet.java revision 0a2ae002e60f7ea9b6bea282086b5eb0ae3c6e51
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 17package android.transition; 18 19import android.animation.TimeInterpolator; 20import android.graphics.Rect; 21import 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. 34 * 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 * <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" 45 * android:ordering="sequential"> 46 * <fade/> 47 * <changeBounds/> 48 * </transitionSet> 49 * </pre> 50 */ 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 for (int i = 0; i < mTransitions.size(); i++) { 172 mTransitions.get(i).addTarget(target); 173 } 174 return (TransitionSet) super.addTarget(target); 175 } 176 177 @Override 178 public TransitionSet addTarget(int targetId) { 179 for (int i = 0; i < mTransitions.size(); i++) { 180 mTransitions.get(i).addTarget(targetId); 181 } 182 return (TransitionSet) super.addTarget(targetId); 183 } 184 185 @Override 186 public TransitionSet addTarget(String targetName) { 187 for (int i = 0; i < mTransitions.size(); i++) { 188 mTransitions.get(i).addTarget(targetName); 189 } 190 return (TransitionSet) super.addTarget(targetName); 191 } 192 193 @Override 194 public TransitionSet addTarget(Class targetType) { 195 for (int i = 0; i < mTransitions.size(); i++) { 196 mTransitions.get(i).addTarget(targetType); 197 } 198 return (TransitionSet) super.addTarget(targetType); 199 } 200 201 @Override 202 public TransitionSet addListener(TransitionListener listener) { 203 return (TransitionSet) super.addListener(listener); 204 } 205 206 @Override 207 public TransitionSet removeTarget(int targetId) { 208 for (int i = 0; i < mTransitions.size(); i++) { 209 mTransitions.get(i).removeTarget(targetId); 210 } 211 return (TransitionSet) super.removeTarget(targetId); 212 } 213 214 @Override 215 public TransitionSet removeTarget(View target) { 216 for (int i = 0; i < mTransitions.size(); i++) { 217 mTransitions.get(i).removeTarget(target); 218 } 219 return (TransitionSet) super.removeTarget(target); 220 } 221 222 @Override 223 public TransitionSet removeTarget(Class target) { 224 for (int i = 0; i < mTransitions.size(); i++) { 225 mTransitions.get(i).removeTarget(target); 226 } 227 return (TransitionSet) super.removeTarget(target); 228 } 229 230 @Override 231 public TransitionSet removeTarget(String target) { 232 for (int i = 0; i < mTransitions.size(); i++) { 233 mTransitions.get(i).removeTarget(target); 234 } 235 return (TransitionSet) super.removeTarget(target); 236 } 237 238 @Override 239 public Transition excludeTarget(View target, boolean exclude) { 240 for (int i = 0; i < mTransitions.size(); i++) { 241 mTransitions.get(i).excludeTarget(target, exclude); 242 } 243 return super.excludeTarget(target, exclude); 244 } 245 246 @Override 247 public Transition excludeTarget(String targetName, boolean exclude) { 248 for (int i = 0; i < mTransitions.size(); i++) { 249 mTransitions.get(i).excludeTarget(targetName, exclude); 250 } 251 return super.excludeTarget(targetName, exclude); 252 } 253 254 @Override 255 public Transition excludeTarget(int targetId, boolean exclude) { 256 for (int i = 0; i < mTransitions.size(); i++) { 257 mTransitions.get(i).excludeTarget(targetId, exclude); 258 } 259 return super.excludeTarget(targetId, exclude); 260 } 261 262 @Override 263 public Transition excludeTarget(Class type, boolean exclude) { 264 for (int i = 0; i < mTransitions.size(); i++) { 265 mTransitions.get(i).excludeTarget(type, exclude); 266 } 267 return super.excludeTarget(type, exclude); 268 } 269 270 @Override 271 public TransitionSet removeListener(TransitionListener listener) { 272 return (TransitionSet) super.removeListener(listener); 273 } 274 275 /** 276 * Removes the specified child transition from this set. 277 * 278 * @param transition The transition to be removed. 279 * @return This transitionSet object. 280 */ 281 public TransitionSet removeTransition(Transition transition) { 282 mTransitions.remove(transition); 283 transition.mParent = null; 284 return this; 285 } 286 287 /** 288 * Sets up listeners for each of the child transitions. This is used to 289 * determine when this transition set is finished (all child transitions 290 * must finish first). 291 */ 292 private void setupStartEndListeners() { 293 TransitionSetListener listener = new TransitionSetListener(this); 294 for (Transition childTransition : mTransitions) { 295 childTransition.addListener(listener); 296 } 297 mCurrentListeners = mTransitions.size(); 298 } 299 300 /** 301 * This listener is used to detect when all child transitions are done, at 302 * which point this transition set is also done. 303 */ 304 static class TransitionSetListener extends TransitionListenerAdapter { 305 TransitionSet mTransitionSet; 306 TransitionSetListener(TransitionSet transitionSet) { 307 mTransitionSet = transitionSet; 308 } 309 @Override 310 public void onTransitionStart(Transition transition) { 311 if (!mTransitionSet.mStarted) { 312 mTransitionSet.start(); 313 mTransitionSet.mStarted = true; 314 } 315 } 316 317 @Override 318 public void onTransitionEnd(Transition transition) { 319 --mTransitionSet.mCurrentListeners; 320 if (mTransitionSet.mCurrentListeners == 0) { 321 // All child trans 322 mTransitionSet.mStarted = false; 323 mTransitionSet.end(); 324 } 325 transition.removeListener(this); 326 } 327 } 328 329 /** 330 * @hide 331 */ 332 @Override 333 protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues, 334 TransitionValuesMaps endValues) { 335 startValues = removeExcludes(startValues); 336 endValues = removeExcludes(endValues); 337 for (Transition childTransition : mTransitions) { 338 childTransition.createAnimators(sceneRoot, startValues, endValues); 339 } 340 } 341 342 private TransitionValuesMaps removeExcludes(TransitionValuesMaps values) { 343 if (mTargetIds.isEmpty() && mTargetIdExcludes == null && mTargetTypeExcludes == null 344 && mTargets.isEmpty()) { 345 return values; 346 } 347 TransitionValuesMaps included = new TransitionValuesMaps(); 348 int numValues = values.viewValues.size(); 349 for (int i = 0; i < numValues; i++) { 350 View view = values.viewValues.keyAt(i); 351 if (isValidTarget(view)) { 352 addViewValues(included, view, values.viewValues.valueAt(i)); 353 } 354 } 355 return included; 356 } 357 358 /** 359 * @hide 360 */ 361 @Override 362 protected void runAnimators() { 363 setupStartEndListeners(); 364 if (!mPlayTogether) { 365 // Setup sequence with listeners 366 // TODO: Need to add listeners in such a way that we can remove them later if canceled 367 for (int i = 1; i < mTransitions.size(); ++i) { 368 Transition previousTransition = mTransitions.get(i - 1); 369 final Transition nextTransition = mTransitions.get(i); 370 previousTransition.addListener(new TransitionListenerAdapter() { 371 @Override 372 public void onTransitionEnd(Transition transition) { 373 nextTransition.runAnimators(); 374 transition.removeListener(this); 375 } 376 }); 377 } 378 Transition firstTransition = mTransitions.get(0); 379 if (firstTransition != null) { 380 firstTransition.runAnimators(); 381 } 382 } else { 383 for (Transition childTransition : mTransitions) { 384 childTransition.runAnimators(); 385 } 386 } 387 } 388 389 @Override 390 public void captureStartValues(TransitionValues transitionValues) { 391 if (isValidTarget(transitionValues.view)) { 392 for (Transition childTransition : mTransitions) { 393 if (childTransition.isValidTarget(transitionValues.view)) { 394 childTransition.captureStartValues(transitionValues); 395 } 396 } 397 } 398 } 399 400 @Override 401 public void captureEndValues(TransitionValues transitionValues) { 402 if (isValidTarget(transitionValues.view)) { 403 for (Transition childTransition : mTransitions) { 404 if (childTransition.isValidTarget(transitionValues.view)) { 405 childTransition.captureEndValues(transitionValues); 406 } 407 } 408 } 409 } 410 411 @Override 412 void capturePropagationValues(TransitionValues transitionValues) { 413 super.capturePropagationValues(transitionValues); 414 int numTransitions = mTransitions.size(); 415 for (int i = 0; i < numTransitions; ++i) { 416 mTransitions.get(i).capturePropagationValues(transitionValues); 417 } 418 } 419 420 /** @hide */ 421 @Override 422 public void pause(View sceneRoot) { 423 super.pause(sceneRoot); 424 int numTransitions = mTransitions.size(); 425 for (int i = 0; i < numTransitions; ++i) { 426 mTransitions.get(i).pause(sceneRoot); 427 } 428 } 429 430 /** @hide */ 431 @Override 432 public void resume(View sceneRoot) { 433 super.resume(sceneRoot); 434 int numTransitions = mTransitions.size(); 435 for (int i = 0; i < numTransitions; ++i) { 436 mTransitions.get(i).resume(sceneRoot); 437 } 438 } 439 440 /** @hide */ 441 @Override 442 protected void cancel() { 443 super.cancel(); 444 int numTransitions = mTransitions.size(); 445 for (int i = 0; i < numTransitions; ++i) { 446 mTransitions.get(i).cancel(); 447 } 448 } 449 450 @Override 451 TransitionSet setSceneRoot(ViewGroup sceneRoot) { 452 super.setSceneRoot(sceneRoot); 453 int numTransitions = mTransitions.size(); 454 for (int i = 0; i < numTransitions; ++i) { 455 mTransitions.get(i).setSceneRoot(sceneRoot); 456 } 457 return (TransitionSet) this; 458 } 459 460 @Override 461 void setCanRemoveViews(boolean canRemoveViews) { 462 super.setCanRemoveViews(canRemoveViews); 463 int numTransitions = mTransitions.size(); 464 for (int i = 0; i < numTransitions; ++i) { 465 mTransitions.get(i).setCanRemoveViews(canRemoveViews); 466 } 467 } 468 469 @Override 470 public void setPropagation(TransitionPropagation propagation) { 471 super.setPropagation(propagation); 472 int numTransitions = mTransitions.size(); 473 for (int i = 0; i < numTransitions; ++i) { 474 mTransitions.get(i).setPropagation(propagation); 475 } 476 } 477 478 @Override 479 public void setEpicenterCallback(EpicenterCallback epicenterCallback) { 480 super.setEpicenterCallback(epicenterCallback); 481 int numTransitions = mTransitions.size(); 482 for (int i = 0; i < numTransitions; ++i) { 483 mTransitions.get(i).setEpicenterCallback(epicenterCallback); 484 } 485 } 486 487 @Override 488 String toString(String indent) { 489 String result = super.toString(indent); 490 for (int i = 0; i < mTransitions.size(); ++i) { 491 result += "\n" + mTransitions.get(i).toString(indent + " "); 492 } 493 return result; 494 } 495 496 @Override 497 public TransitionSet clone() { 498 TransitionSet clone = (TransitionSet) super.clone(); 499 clone.mTransitions = new ArrayList<Transition>(); 500 int numTransitions = mTransitions.size(); 501 for (int i = 0; i < numTransitions; ++i) { 502 clone.addTransition((Transition) mTransitions.get(i).clone()); 503 } 504 return clone; 505 } 506} 507