Subclasses:
AutoTransition
Gradle dependencies
compile group: 'androidx.transition', name: 'transition', version: '1.5.1'
- groupId: androidx.transition
- artifactId: transition
- version: 1.5.1
Artifact androidx.transition:transition:1.5.1 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.transition:transition com.android.support:transition
Androidx class mapping:
androidx.transition.TransitionSet android.support.transition.TransitionSet
Overview
A TransitionSet is a parent of child transitions (including other
 TransitionSets). Using TransitionSets enables more complex
 choreography of transitions, where some sets play TransitionSet.ORDERING_TOGETHER and
 others play TransitionSet.ORDERING_SEQUENTIAL. For example, AutoTransition
 uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
 a ChangeBounds, followed by a Fade(Fade.OUT) transition.
 
A TransitionSet can be described in a resource file by using the
 tag transitionSet, along with the standard
 attributes of TransitionSet and Transition. Child transitions of the
 TransitionSet object can be loaded by adding those child tags inside the
 enclosing transitionSet tag. For example, the following xml
 describes a TransitionSet that plays a Fade and then a ChangeBounds
 transition on the affected view targets:
 
     <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
             android:transitionOrdering="sequential">
         <fade/>
         <changeBounds/>
     </transitionSet>
 Summary
| Methods | 
|---|
| public Transition | addListener(Transition.TransitionListener listener) 
 Adds a listener to the set of listeners that are sent events through the
 life of an animation, such as start, repeat, and end. | 
| public Transition | addTarget(View target) 
 Sets the target view instances that this Transition is interested in
 animating. | 
| public TransitionSet | addTransition(Transition transition) 
 Adds child transition to this set. | 
| protected void | cancel() 
 This method cancels a transition that is currently running. | 
| public abstract void | captureEndValues(TransitionValues transitionValues) 
 Captures the values in the end scene for the properties that this
 transition monitors. | 
| public abstract void | captureStartValues(TransitionValues transitionValues) 
 Captures the values in the start scene for the properties that this
 transition monitors. | 
| public Transition | clone() 
 | 
| public Transition | excludeTarget(View target, boolean exclude) 
 Whether to add the given target to the list of targets to exclude from this
 transition. | 
| public int | getOrdering() 
 Returns the ordering of this TransitionSet. | 
| public Transition | getTransitionAt(int index) 
 Returns the child Transition at the specified position in the TransitionSet. | 
| public int | getTransitionCount() 
 Returns the number of child transitions in the TransitionSet. | 
| public boolean | isSeekingSupported() 
 Returns true if the Transition can be used by
 TransitionManager.controlDelayedTransition(ViewGroup, Transition). | 
| public void | pause(View sceneRoot) 
 | 
| public Transition | removeListener(Transition.TransitionListener listener) 
 Removes a listener from the set listening to this animation. | 
| public Transition | removeTarget(View target) 
 Removes the given target from the list of targets that this Transition
 is interested in animating. | 
| public Transition | removeTarget(View target) 
 Removes the given target from the list of targets that this Transition
 is interested in animating. | 
| public TransitionSet | removeTransition(Transition transition) 
 Removes the specified child transition from this set. | 
| public void | resume(View sceneRoot) 
 | 
| protected void | runAnimators() 
 This is called internally once all animations have been set up by the
 transition hierarchy. | 
| public TransitionSet | setDuration(long duration) 
 Setting a non-negative duration on a TransitionSet causes all of the child
 transitions (current and future) to inherit this duration. | 
| public void | setEpicenterCallback(Transition.EpicenterCallback epicenterCallback) 
 Sets the callback to use to find the epicenter of a Transition. | 
| public Transition | setInterpolator(TimeInterpolator interpolator) 
 Sets the interpolator of this transition. | 
| public TransitionSet | setOrdering(int ordering) 
 Sets the play order of this set's child transitions. | 
| public void | setPathMotion(PathMotion pathMotion) 
 Sets the algorithm used to calculate two-dimensional interpolation. | 
| public void | setPropagation(TransitionPropagation transitionPropagation) 
 Sets the method for determining Animator start delays. | 
| public Transition | setStartDelay(long startDelay) 
 Sets the startDelay of this transition. | 
| from Transition | animate, createAnimator, end, excludeChildren, getDuration, getEpicenter, getEpicenterCallback, getInterpolator, getName, getPathMotion, getPropagation, getRootTransition, getStartDelay, getTargetIds, getTargetNames, getTargets, getTargetTypes, getTransitionProperties, getTransitionValues, isTransitionRequired, setMatchOrder, start, toString | 
| from java.lang.Object | equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait | 
Fields
public static final int 
ORDERING_TOGETHERA flag used to indicate that the child transitions of this set
 should all start at the same time.
public static final int 
ORDERING_SEQUENTIALA flag used to indicate that the child transitions of this set should
 play in sequence; when one child transition ends, the next child
 transition begins. Note that a transition does not end until all
 instances of it (which are playing on all applicable targets of the
 transition) end.
Constructors
Constructs an empty transition set. Add child transitions to the
 set by calling TransitionSet.addTransition(Transition) )}. By default,
 child transitions will play together.
public 
TransitionSet(Context context, AttributeSet attrs)
Methods
Sets the play order of this set's child transitions.
Parameters:
ordering: TransitionSet.ORDERING_TOGETHER to play this set's child
                 transitions together, TransitionSet.ORDERING_SEQUENTIAL to play the child
                 transitions in sequence.
Returns:
This transitionSet object.
Returns the ordering of this TransitionSet. By default, the value is
 TransitionSet.ORDERING_TOGETHER.
Returns:
TransitionSet.ORDERING_TOGETHER if child transitions will play at the same
 time, TransitionSet.ORDERING_SEQUENTIAL if they will play in sequence.
See also: TransitionSet.setOrdering(int)
Adds child transition to this set. The order in which this child transition
 is added relative to other child transitions that are added, in addition to
 the ordering property, determines the
 order in which the transitions are started.
 
If this transitionSet has a duration,
 interpolator, propagation delay,
 path motion, or
 epicenter callback
 set on it, the child transition will inherit the values that are set.
 Transitions are assumed to have a maximum of one transitionSet parent.
Parameters:
transition: A non-null child transition to be added to this set.
Returns:
This transitionSet object.
public int 
getTransitionCount()
Returns the number of child transitions in the TransitionSet.
Returns:
The number of child transitions in the TransitionSet.
See also: TransitionSet.addTransition(Transition), TransitionSet.getTransitionAt(int)
Returns the child Transition at the specified position in the TransitionSet.
Parameters:
index: The position of the Transition to retrieve.
See also: TransitionSet.addTransition(Transition), TransitionSet.getTransitionCount()
Setting a non-negative duration on a TransitionSet causes all of the child
 transitions (current and future) to inherit this duration.
Parameters:
duration: The length of the animation, in milliseconds.
Returns:
This transitionSet object.
Sets the startDelay of this transition. By default, there is no delay
 (indicated by a negative number), which means that the Animator created by
 the transition will have its own specified startDelay. If the delay of a
 Transition is set, that delay will override the Animator delay.
Parameters:
startDelay: The length of the delay, in milliseconds.
Returns:
This transition object.
public 
Transition setInterpolator(TimeInterpolator interpolator)
Sets the interpolator of this transition. By default, the interpolator
 is null, which means that the Animator created by the transition
 will have its own specified interpolator. If the interpolator of a
 Transition is set, that interpolator will override the Animator interpolator.
Parameters:
interpolator: The time interpolator used by the transition
Returns:
This transition object.
Sets the target view instances that this Transition is interested in
 animating. By default, there are no targets, and a Transition will
 listen for changes on every view in the hierarchy below the sceneRoot
 of the Scene being transitioned into. Setting targets constrains
 the Transition to only listen for, and act on, these views.
 All other views will be ignored.
 
The target list is like the targetId
 list except this list specifies the actual View instances, not the ids
 of the views. This is an important distinction when scene changes involve
 view hierarchies which have been inflated separately; different views may
 share the same id but not actually be the same instance. If the transition
 should treat those views as the same, then Transition should be used
 instead of Transition.addTarget(View). If, on the other hand, scene changes involve
 changes all within the same view hierarchy, among views which do not
 necessarily have ids set on them, then the target list of views may be more
 convenient.
Parameters:
target: A View on which the Transition will act, must be non-null.
Returns:
The Transition to which the target is added.
 Returning the same object makes it easier to chain calls during
 construction, such as
 transitionSet.addTransitions(new Fade()).addTarget(someView);
See also: Transition
Adds a listener to the set of listeners that are sent events through the
 life of an animation, such as start, repeat, and end.
Parameters:
listener: the listener to be added to the current set of listeners
                 for this animation.
Returns:
This transition object.
Removes the given target from the list of targets that this Transition
 is interested in animating.
Parameters:
target: The target view, must be non-null.
Returns:
Transition The Transition from which the target is removed.
 Returning the same object makes it easier to chain calls during
 construction, such as
 transitionSet.addTransitions(new Fade()).removeTarget(someView);
Removes the given target from the list of targets that this Transition
 is interested in animating.
Parameters:
target: The target view, must be non-null.
Returns:
Transition The Transition from which the target is removed.
 Returning the same object makes it easier to chain calls during
 construction, such as
 transitionSet.addTransitions(new Fade()).removeTarget(someView);
public 
Transition excludeTarget(View target, boolean exclude)
Whether to add the given target to the list of targets to exclude from this
 transition. The exclude parameter specifies whether the target
 should be added to or removed from the excluded list.
 
Excluding targets is a general mechanism for allowing transitions to run on
 a view hierarchy while skipping target views that should not be part of
 the transition. For example, you may want to avoid animating children
 of a specific ListView or Spinner. Views can be excluded either by their
 id, or by their instance reference, or by the Class of that view
 (eg, ).
Parameters:
target: The target to ignore when running this transition.
exclude: Whether to add the target to or remove the target from the
                current list of excluded targets.
Returns:
This transition object.
See also: Transition.excludeChildren(View, boolean), Transition, Transition
Removes a listener from the set listening to this animation.
Parameters:
listener: the listener to be removed from the current set of
                 listeners for this transition.
Returns:
This transition object.
Sets the algorithm used to calculate two-dimensional interpolation.
 
 Transitions such as  move Views, typically
 in a straight path between the start and end positions. Applications that desire to
 have these motions move in a curve can change how Views interpolate in two dimensions
 by extending PathMotion and implementing
 .
 
Parameters:
pathMotion: Algorithm object to use for determining how to interpolate in two
                   dimensions. If null, a straight-path algorithm will be used.
See also: , PatternPathMotion
Removes the specified child transition from this set.
Parameters:
transition: The transition to be removed.
Returns:
This transitionSet object.
protected void 
runAnimators()
This is called internally once all animations have been set up by the
 transition hierarchy.
public boolean 
isSeekingSupported()
Returns true if the Transition can be used by
 TransitionManager.controlDelayedTransition(ViewGroup, Transition). This means
 that any the state must be ready before any Animator returned by
 Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues) has started and
 if the Animator has ended, it must be able to restore the state when starting in reverse.
 If a Transition must know when the entire transition has ended, a Transition.TransitionListener
 can be added to Transition.getRootTransition() and it can listen for
 Transition.TransitionListener.onTransitionEnd(Transition).
Captures the values in the start scene for the properties that this
 transition monitors. These values are then passed as the startValues
 structure in a later call to
 Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues).
 The main concern for an implementation is what the
 properties are that the transition cares about and what the values are
 for all of those properties. The start and end values will be compared
 later during the
 Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues)
 method to determine what, if any, animations, should be run.
 
Subclasses must implement this method. The method should only be called by the
 transition system; it is not intended to be called from external classes.
Parameters:
transitionValues: The holder for any values that the Transition
                         wishes to store. Values are stored in the values field
                         of this TransitionValues object and are keyed from
                         a String value. For example, to store a view's rotation value,
                         a transition might call
                         transitionValues.values.put("appname:transitionname:rotation",
                         view.getRotation()). The target view will already be stored
                         in
                         the transitionValues structure when this method is called.
See also: Transition.captureEndValues(TransitionValues), Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues)
Captures the values in the end scene for the properties that this
 transition monitors. These values are then passed as the endValues
 structure in a later call to
 Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues).
 The main concern for an implementation is what the
 properties are that the transition cares about and what the values are
 for all of those properties. The start and end values will be compared
 later during the
 Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues)
 method to determine what, if any, animations, should be run.
 
Subclasses must implement this method. The method should only be called by the
 transition system; it is not intended to be called from external classes.
Parameters:
transitionValues: The holder for any values that the Transition
                         wishes to store. Values are stored in the values field
                         of this TransitionValues object and are keyed from
                         a String value. For example, to store a view's rotation value,
                         a transition might call
                         transitionValues.values.put("appname:transitionname:rotation",
                         view.getRotation()). The target view will already be stored
                         in
                         the transitionValues structure when this method is called.
See also: Transition.captureStartValues(TransitionValues), Transition.createAnimator(ViewGroup, TransitionValues, TransitionValues)
public void 
pause(View sceneRoot)
Parameters:
sceneRoot: 
public void 
resume(View sceneRoot)
Parameters:
sceneRoot: 
This method cancels a transition that is currently running.
Sets the method for determining Animator start delays.
 When a Transition affects several Views like  or
 , there may be a desire to have a "wave-front" effect
 such that the Animator start delay depends on position of the View. The
 TransitionPropagation specifies how the start delays are calculated.
Parameters:
transitionPropagation: The class used to determine the start delay of
                              Animators created by this Transition. A null value
                              indicates that no delay should be used.
Sets the callback to use to find the epicenter of a Transition. A null value indicates
 that there is no epicenter in the Transition and onGetEpicenter() will return null.
 Transitions like  use a point or Rect to orient
 the direction of travel. This is called the epicenter of the Transition and is
 typically centered on a touched View. The
  allows a Transition to
 dynamically retrieve the epicenter during a Transition.
Parameters:
epicenterCallback: The callback to use to find the epicenter of the Transition.
Source
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.transition;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.util.AndroidRuntimeException;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.content.res.TypedArrayUtils;
import java.util.ArrayList;
/**
 * A TransitionSet is a parent of child transitions (including other
 * TransitionSets). Using TransitionSets enables more complex
 * choreography of transitions, where some sets play {@link #ORDERING_TOGETHER} and
 * others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
 * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
 * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
 *
 * <p>A TransitionSet can be described in a resource file by using the
 * tag <code>transitionSet</code>, along with the standard
 * attributes of {@code TransitionSet} and {@link Transition}. Child transitions of the
 * TransitionSet object can be loaded by adding those child tags inside the
 * enclosing <code>transitionSet</code> tag. For example, the following xml
 * describes a TransitionSet that plays a Fade and then a ChangeBounds
 * transition on the affected view targets:</p>
 * <pre>
 *     <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
 *             android:transitionOrdering="sequential">
 *         <fade/>
 *         <changeBounds/>
 *     </transitionSet>
 * </pre>
 */
public class TransitionSet extends Transition {
    /**
     * Flag indicating the the interpolator changed.
     */
    private static final int FLAG_CHANGE_INTERPOLATOR = 0x01;
    /**
     * Flag indicating the the propagation changed.
     */
    private static final int FLAG_CHANGE_PROPAGATION = 0x02;
    /**
     * Flag indicating the the path motion changed.
     */
    private static final int FLAG_CHANGE_PATH_MOTION = 0x04;
    /**
     * Flag indicating the the epicentera callback changed.
     */
    private static final int FLAG_CHANGE_EPICENTER = 0x08;
    ArrayList<Transition> mTransitions = new ArrayList<>();
    private boolean mPlayTogether = true;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mCurrentListeners;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    boolean mStarted = false;
    // Flags to know whether or not the interpolator, path motion, epicenter, propagation
    // have changed
    private int mChangeFlags = 0;
    /**
     * A flag used to indicate that the child transitions of this set
     * should all start at the same time.
     */
    public static final int ORDERING_TOGETHER = 0;
    /**
     * A flag used to indicate that the child transitions of this set should
     * play in sequence; when one child transition ends, the next child
     * transition begins. Note that a transition does not end until all
     * instances of it (which are playing on all applicable targets of the
     * transition) end.
     */
    public static final int ORDERING_SEQUENTIAL = 1;
    /**
     * Constructs an empty transition set. Add child transitions to the
     * set by calling {@link #addTransition(Transition)} )}. By default,
     * child transitions will play {@link #ORDERING_TOGETHER together}.
     */
    public TransitionSet() {
    }
    public TransitionSet(@NonNull Context context, @NonNull AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, Styleable.TRANSITION_SET);
        int ordering = TypedArrayUtils.getNamedInt(a, (XmlResourceParser) attrs,
                "transitionOrdering", Styleable.TransitionSet.TRANSITION_ORDERING,
                TransitionSet.ORDERING_TOGETHER);
        setOrdering(ordering);
        a.recycle();
    }
    /**
     * Sets the play order of this set's child transitions.
     *
     * @param ordering {@link #ORDERING_TOGETHER} to play this set's child
     *                 transitions together, {@link #ORDERING_SEQUENTIAL} to play the child
     *                 transitions in sequence.
     * @return This transitionSet object.
     */
    @NonNull
    public TransitionSet setOrdering(int ordering) {
        switch (ordering) {
            case ORDERING_SEQUENTIAL:
                mPlayTogether = false;
                break;
            case ORDERING_TOGETHER:
                mPlayTogether = true;
                break;
            default:
                throw new AndroidRuntimeException("Invalid parameter for TransitionSet "
                        + "ordering: " + ordering);
        }
        return this;
    }
    /**
     * Returns the ordering of this TransitionSet. By default, the value is
     * {@link #ORDERING_TOGETHER}.
     *
     * @return {@link #ORDERING_TOGETHER} if child transitions will play at the same
     * time, {@link #ORDERING_SEQUENTIAL} if they will play in sequence.
     * @see #setOrdering(int)
     */
    public int getOrdering() {
        return mPlayTogether ? ORDERING_TOGETHER : ORDERING_SEQUENTIAL;
    }
    /**
     * Adds child transition to this set. The order in which this child transition
     * is added relative to other child transitions that are added, in addition to
     * the {@link #getOrdering() ordering} property, determines the
     * order in which the transitions are started.
     *
     * <p>If this transitionSet has a {@link #getDuration() duration},
     * {@link #getInterpolator() interpolator}, {@link #getPropagation() propagation delay},
     * {@link #getPathMotion() path motion}, or
     * {@link #setEpicenterCallback(EpicenterCallback) epicenter callback}
     * set on it, the child transition will inherit the values that are set.
     * Transitions are assumed to have a maximum of one transitionSet parent.</p>
     *
     * @param transition A non-null child transition to be added to this set.
     * @return This transitionSet object.
     */
    @NonNull
    public TransitionSet addTransition(@NonNull Transition transition) {
        addTransitionInternal(transition);
        if (mDuration >= 0) {
            transition.setDuration(mDuration);
        }
        if ((mChangeFlags & FLAG_CHANGE_INTERPOLATOR) != 0) {
            transition.setInterpolator(getInterpolator());
        }
        if ((mChangeFlags & FLAG_CHANGE_PROPAGATION) != 0) {
            transition.setPropagation(getPropagation());
        }
        if ((mChangeFlags & FLAG_CHANGE_PATH_MOTION) != 0) {
            transition.setPathMotion(getPathMotion());
        }
        if ((mChangeFlags & FLAG_CHANGE_EPICENTER) != 0) {
            transition.setEpicenterCallback(getEpicenterCallback());
        }
        return this;
    }
    private void addTransitionInternal(@NonNull Transition transition) {
        mTransitions.add(transition);
        transition.mParent = this;
    }
    /**
     * Returns the number of child transitions in the TransitionSet.
     *
     * @return The number of child transitions in the TransitionSet.
     * @see #addTransition(Transition)
     * @see #getTransitionAt(int)
     */
    public int getTransitionCount() {
        return mTransitions.size();
    }
    /**
     * Returns the child Transition at the specified position in the TransitionSet.
     *
     * @param index The position of the Transition to retrieve.
     * @see #addTransition(Transition)
     * @see #getTransitionCount()
     */
    @Nullable
    public Transition getTransitionAt(int index) {
        if (index < 0 || index >= mTransitions.size()) {
            return null;
        }
        return mTransitions.get(index);
    }
    /**
     * Setting a non-negative duration on a TransitionSet causes all of the child
     * transitions (current and future) to inherit this duration.
     *
     * @param duration The length of the animation, in milliseconds.
     * @return This transitionSet object.
     */
    @NonNull
    @Override
    public TransitionSet setDuration(long duration) {
        super.setDuration(duration);
        if (mDuration >= 0 && mTransitions != null) {
            int numTransitions = mTransitions.size();
            for (int i = 0; i < numTransitions; ++i) {
                mTransitions.get(i).setDuration(duration);
            }
        }
        return this;
    }
    @NonNull
    @Override
    public TransitionSet setStartDelay(long startDelay) {
        return (TransitionSet) super.setStartDelay(startDelay);
    }
    @NonNull
    @Override
    public TransitionSet setInterpolator(@Nullable TimeInterpolator interpolator) {
        mChangeFlags |= FLAG_CHANGE_INTERPOLATOR;
        if (mTransitions != null) {
            int numTransitions = mTransitions.size();
            for (int i = 0; i < numTransitions; ++i) {
                mTransitions.get(i).setInterpolator(interpolator);
            }
        }
        return (TransitionSet) super.setInterpolator(interpolator);
    }
    @NonNull
    @Override
    public TransitionSet addTarget(@NonNull View target) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).addTarget(target);
        }
        return (TransitionSet) super.addTarget(target);
    }
    @NonNull
    @Override
    public TransitionSet addTarget(@IdRes int targetId) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).addTarget(targetId);
        }
        return (TransitionSet) super.addTarget(targetId);
    }
    @NonNull
    @Override
    public TransitionSet addTarget(@NonNull String targetName) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).addTarget(targetName);
        }
        return (TransitionSet) super.addTarget(targetName);
    }
    @NonNull
    @Override
    public TransitionSet addTarget(@NonNull Class<?> targetType) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).addTarget(targetType);
        }
        return (TransitionSet) super.addTarget(targetType);
    }
    @NonNull
    @Override
    public TransitionSet addListener(@NonNull TransitionListener listener) {
        return (TransitionSet) super.addListener(listener);
    }
    @NonNull
    @Override
    public TransitionSet removeTarget(@IdRes int targetId) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).removeTarget(targetId);
        }
        return (TransitionSet) super.removeTarget(targetId);
    }
    @NonNull
    @Override
    public TransitionSet removeTarget(@NonNull View target) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).removeTarget(target);
        }
        return (TransitionSet) super.removeTarget(target);
    }
    @NonNull
    @Override
    public TransitionSet removeTarget(@NonNull Class<?> target) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).removeTarget(target);
        }
        return (TransitionSet) super.removeTarget(target);
    }
    @NonNull
    @Override
    public TransitionSet removeTarget(@NonNull String targetName) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).removeTarget(targetName);
        }
        return (TransitionSet) super.removeTarget(targetName);
    }
    @NonNull
    @Override
    public Transition excludeTarget(@NonNull View target, boolean exclude) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).excludeTarget(target, exclude);
        }
        return super.excludeTarget(target, exclude);
    }
    @NonNull
    @Override
    public Transition excludeTarget(@NonNull String targetName, boolean exclude) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).excludeTarget(targetName, exclude);
        }
        return super.excludeTarget(targetName, exclude);
    }
    @NonNull
    @Override
    public Transition excludeTarget(int targetId, boolean exclude) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).excludeTarget(targetId, exclude);
        }
        return super.excludeTarget(targetId, exclude);
    }
    @NonNull
    @Override
    public Transition excludeTarget(@NonNull Class<?> type, boolean exclude) {
        for (int i = 0; i < mTransitions.size(); i++) {
            mTransitions.get(i).excludeTarget(type, exclude);
        }
        return super.excludeTarget(type, exclude);
    }
    @NonNull
    @Override
    public TransitionSet removeListener(@NonNull TransitionListener listener) {
        return (TransitionSet) super.removeListener(listener);
    }
    @Override
    public void setPathMotion(@Nullable PathMotion pathMotion) {
        super.setPathMotion(pathMotion);
        mChangeFlags |= FLAG_CHANGE_PATH_MOTION;
        if (mTransitions != null) {
            for (int i = 0; i < mTransitions.size(); i++) {
                mTransitions.get(i).setPathMotion(pathMotion);
            }
        }
    }
    /**
     * Removes the specified child transition from this set.
     *
     * @param transition The transition to be removed.
     * @return This transitionSet object.
     */
    @NonNull
    public TransitionSet removeTransition(@NonNull Transition transition) {
        mTransitions.remove(transition);
        transition.mParent = null;
        return this;
    }
    /**
     * Sets up listeners for each of the child transitions. This is used to
     * determine when this transition set is finished (all child transitions
     * must finish first).
     */
    private void setupStartEndListeners() {
        TransitionSetListener listener = new TransitionSetListener(this);
        for (Transition childTransition : mTransitions) {
            childTransition.addListener(listener);
        }
        mCurrentListeners = mTransitions.size();
    }
    /**
     * This listener is used to detect when all child transitions are done, at
     * which point this transition set is also done.
     */
    static class TransitionSetListener extends TransitionListenerAdapter {
        TransitionSet mTransitionSet;
        TransitionSetListener(TransitionSet transitionSet) {
            mTransitionSet = transitionSet;
        }
        @Override
        public void onTransitionStart(@NonNull Transition transition) {
            if (!mTransitionSet.mStarted) {
                mTransitionSet.start();
                mTransitionSet.mStarted = true;
            }
        }
        @Override
        public void onTransitionEnd(@NonNull Transition transition) {
            --mTransitionSet.mCurrentListeners;
            if (mTransitionSet.mCurrentListeners == 0) {
                // All child trans
                mTransitionSet.mStarted = false;
                mTransitionSet.end();
            }
            transition.removeListener(this);
        }
    }
    @Override
    void createAnimators(@NonNull ViewGroup sceneRoot, @NonNull TransitionValuesMaps startValues,
            @NonNull TransitionValuesMaps endValues,
            @NonNull ArrayList<TransitionValues> startValuesList,
            @NonNull ArrayList<TransitionValues> endValuesList) {
        long startDelay = getStartDelay();
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; i++) {
            Transition childTransition = mTransitions.get(i);
            // We only set the start delay on the first transition if we are playing
            // the transitions sequentially.
            if (startDelay > 0 && (mPlayTogether || i == 0)) {
                long childStartDelay = childTransition.getStartDelay();
                if (childStartDelay > 0) {
                    childTransition.setStartDelay(startDelay + childStartDelay);
                } else {
                    childTransition.setStartDelay(startDelay);
                }
            }
            childTransition.createAnimators(sceneRoot, startValues, endValues, startValuesList,
                    endValuesList);
        }
    }
    /**
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Override
    protected void runAnimators() {
        if (mTransitions.isEmpty()) {
            start();
            end();
            return;
        }
        setupStartEndListeners();
        if (!mPlayTogether) {
            // Setup sequence with listeners
            // TODO: Need to add listeners in such a way that we can remove them later if canceled
            for (int i = 1; i < mTransitions.size(); ++i) {
                Transition previousTransition = mTransitions.get(i - 1);
                final Transition nextTransition = mTransitions.get(i);
                previousTransition.addListener(new TransitionListenerAdapter() {
                    @Override
                    public void onTransitionEnd(@NonNull Transition transition) {
                        nextTransition.runAnimators();
                        transition.removeListener(this);
                    }
                });
            }
            Transition firstTransition = mTransitions.get(0);
            if (firstTransition != null) {
                firstTransition.runAnimators();
            }
        } else {
            for (Transition childTransition : mTransitions) {
                childTransition.runAnimators();
            }
        }
    }
    @Override
    boolean hasAnimators() {
        for (int i = 0; i < mTransitions.size(); i++) {
            Transition child = mTransitions.get(i);
            if (child.hasAnimators()) {
                return true;
            }
        }
        return false;
    }
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    @Override
    void prepareAnimatorsForSeeking() {
        mTotalDuration = 0;
        TransitionListenerAdapter listener = new TransitionListenerAdapter() {
            @Override
            public void onTransitionCancel(@NonNull Transition transition) {
                mTransitions.remove(transition);
                if (!hasAnimators()) {
                    notifyListeners(TransitionNotification.ON_CANCEL, false);
                    mEnded = true;
                    notifyListeners(TransitionNotification.ON_END, false);
                }
            }
        };
        for (int i = 0; i < mTransitions.size(); ++i) {
            Transition transition = mTransitions.get(i);
            transition.addListener(listener);
            transition.prepareAnimatorsForSeeking();
            long duration = transition.getTotalDurationMillis();
            if (mPlayTogether) {
                mTotalDuration = Math.max(mTotalDuration, duration);
            } else {
                transition.mSeekOffsetInParent = mTotalDuration;
                mTotalDuration += duration;
            }
        }
    }
    /**
     * Returns the index of the Transition that is playing at playTime. If no such transition
     * exists, either because that Transition has been canceled or the TransitionSet is empty,
     * the index of the one prior, or 0 will be returned.
     */
    private int indexOfTransition(long playTime) {
        for (int i = 1; i < mTransitions.size(); i++) {
            Transition transition = mTransitions.get(i);
            if (transition.mSeekOffsetInParent > playTime) {
                return i - 1;
            }
        }
        return mTransitions.size() - 1;
    }
    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
    @Override
    void setCurrentPlayTimeMillis(long playTimeMillis, long lastPlayTimeMillis) {
        long duration = getTotalDurationMillis();
        if (mParent != null && ((playTimeMillis < 0 && lastPlayTimeMillis < 0)
                || (playTimeMillis > duration && lastPlayTimeMillis > duration))
        ) {
            return;
        }
        boolean isReverse = playTimeMillis < lastPlayTimeMillis;
        if ((playTimeMillis >= 0 && lastPlayTimeMillis < 0)
                || (playTimeMillis <= duration && lastPlayTimeMillis > duration)
            ) {
            mEnded = false;
            notifyListeners(TransitionNotification.ON_START, isReverse);
        }
        if (mPlayTogether) {
            for (int i = 0; i < mTransitions.size(); i++) {
                Transition transition = mTransitions.get(i);
                transition.setCurrentPlayTimeMillis(playTimeMillis, lastPlayTimeMillis);
            }
        } else {
            // find the Transition that lastPlayTimeMillis was using
            int startIndex = indexOfTransition(lastPlayTimeMillis);
            if (playTimeMillis >= lastPlayTimeMillis) {
                // move forward through transitions
                for (int i = startIndex; i < mTransitions.size(); i++) {
                    Transition transition = mTransitions.get(i);
                    long transitionStart = transition.mSeekOffsetInParent;
                    long playTime = playTimeMillis - transitionStart;
                    if (playTime < 0) {
                        break; // went past
                    }
                    long lastPlayTime = lastPlayTimeMillis - transitionStart;
                    transition.setCurrentPlayTimeMillis(playTime, lastPlayTime);
                }
            } else {
                // move backwards through transitions
                for (int i = startIndex; i >= 0; i--) {
                    Transition transition = mTransitions.get(i);
                    long transitionStart = transition.mSeekOffsetInParent;
                    long playTime = playTimeMillis - transitionStart;
                    long lastPlayTime = lastPlayTimeMillis - transitionStart;
                    transition.setCurrentPlayTimeMillis(playTime, lastPlayTime);
                    if (playTime >= 0) {
                        break;
                    }
                }
            }
        }
        if (mParent != null && ((playTimeMillis > duration && lastPlayTimeMillis <= duration)
                || (playTimeMillis < 0 && lastPlayTimeMillis >= 0))
        ) {
            if (playTimeMillis > duration) {
                mEnded = true;
            }
            notifyListeners(TransitionNotification.ON_END, isReverse);
        }
    }
    @Override
    public boolean isSeekingSupported() {
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; i++) {
            if (!mTransitions.get(i).isSeekingSupported()) {
                return false;
            }
        }
        return true;
    }
    @Override
    public void captureStartValues(@NonNull TransitionValues transitionValues) {
        if (isValidTarget(transitionValues.view)) {
            for (Transition childTransition : mTransitions) {
                if (childTransition.isValidTarget(transitionValues.view)) {
                    childTransition.captureStartValues(transitionValues);
                    transitionValues.mTargetedTransitions.add(childTransition);
                }
            }
        }
    }
    @Override
    public void captureEndValues(@NonNull TransitionValues transitionValues) {
        if (isValidTarget(transitionValues.view)) {
            for (Transition childTransition : mTransitions) {
                if (childTransition.isValidTarget(transitionValues.view)) {
                    childTransition.captureEndValues(transitionValues);
                    transitionValues.mTargetedTransitions.add(childTransition);
                }
            }
        }
    }
    @Override
    void capturePropagationValues(TransitionValues transitionValues) {
        super.capturePropagationValues(transitionValues);
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).capturePropagationValues(transitionValues);
        }
    }
    /**
     * @param sceneRoot */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Override
    public void pause(@Nullable View sceneRoot) {
        super.pause(sceneRoot);
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).pause(sceneRoot);
        }
    }
    /**
     * @param sceneRoot */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Override
    public void resume(@Nullable View sceneRoot) {
        super.resume(sceneRoot);
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).resume(sceneRoot);
        }
    }
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Override
    protected void cancel() {
        super.cancel();
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).cancel();
        }
    }
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @Override
    void forceToEnd(ViewGroup sceneRoot) {
        super.forceToEnd(sceneRoot);
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).forceToEnd(sceneRoot);
        }
    }
    @Override
    void setCanRemoveViews(boolean canRemoveViews) {
        super.setCanRemoveViews(canRemoveViews);
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).setCanRemoveViews(canRemoveViews);
        }
    }
    @Override
    public void setPropagation(@Nullable TransitionPropagation transitionPropagation) {
        super.setPropagation(transitionPropagation);
        mChangeFlags |= FLAG_CHANGE_PROPAGATION;
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).setPropagation(transitionPropagation);
        }
    }
    @Override
    public void setEpicenterCallback(@Nullable EpicenterCallback epicenterCallback) {
        super.setEpicenterCallback(epicenterCallback);
        mChangeFlags |= FLAG_CHANGE_EPICENTER;
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            mTransitions.get(i).setEpicenterCallback(epicenterCallback);
        }
    }
    @Override
    String toString(String indent) {
        String result = super.toString(indent);
        for (int i = 0; i < mTransitions.size(); ++i) {
            result += "\n" + mTransitions.get(i).toString(indent + "  ");
        }
        return result;
    }
    @NonNull
    @Override
    public Transition clone() {
        TransitionSet clone = (TransitionSet) super.clone();
        clone.mTransitions = new ArrayList<>();
        int numTransitions = mTransitions.size();
        for (int i = 0; i < numTransitions; ++i) {
            clone.addTransitionInternal(mTransitions.get(i).clone());
        }
        return clone;
    }
}