public class

TransitionSet

extends Transition

 java.lang.Object

androidx.transition.Transition

↳androidx.transition.TransitionSet

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

Fields
public static final intORDERING_SEQUENTIAL

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.

public static final intORDERING_TOGETHER

A flag used to indicate that the child transitions of this set should all start at the same time.

from TransitionMATCH_ID, MATCH_INSTANCE, MATCH_ITEM_ID, MATCH_NAME
Constructors
publicTransitionSet()

Constructs an empty transition set.

publicTransitionSet(Context context, AttributeSet attrs)

Methods
public TransitionaddListener(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 TransitionaddTarget(View target)

Sets the target view instances that this Transition is interested in animating.

public TransitionSetaddTransition(Transition transition)

Adds child transition to this set.

protected voidcancel()

This method cancels a transition that is currently running.

public abstract voidcaptureEndValues(TransitionValues transitionValues)

Captures the values in the end scene for the properties that this transition monitors.

public abstract voidcaptureStartValues(TransitionValues transitionValues)

Captures the values in the start scene for the properties that this transition monitors.

public Transitionclone()

public TransitionexcludeTarget(View target, boolean exclude)

Whether to add the given target to the list of targets to exclude from this transition.

public intgetOrdering()

Returns the ordering of this TransitionSet.

public TransitiongetTransitionAt(int index)

Returns the child Transition at the specified position in the TransitionSet.

public intgetTransitionCount()

Returns the number of child transitions in the TransitionSet.

public booleanisSeekingSupported()

Returns true if the Transition can be used by TransitionManager.controlDelayedTransition(ViewGroup, Transition).

public voidpause(View sceneRoot)

public TransitionremoveListener(Transition.TransitionListener listener)

Removes a listener from the set listening to this animation.

public TransitionremoveTarget(View target)

Removes the given target from the list of targets that this Transition is interested in animating.

public TransitionremoveTarget(View target)

Removes the given target from the list of targets that this Transition is interested in animating.

public TransitionSetremoveTransition(Transition transition)

Removes the specified child transition from this set.

public voidresume(View sceneRoot)

protected voidrunAnimators()

This is called internally once all animations have been set up by the transition hierarchy.

public TransitionSetsetDuration(long duration)

Setting a non-negative duration on a TransitionSet causes all of the child transitions (current and future) to inherit this duration.

public voidsetEpicenterCallback(Transition.EpicenterCallback epicenterCallback)

Sets the callback to use to find the epicenter of a Transition.

public TransitionsetInterpolator(TimeInterpolator interpolator)

Sets the interpolator of this transition.

public TransitionSetsetOrdering(int ordering)

Sets the play order of this set's child transitions.

public voidsetPathMotion(PathMotion pathMotion)

Sets the algorithm used to calculate two-dimensional interpolation.

public voidsetPropagation(TransitionPropagation transitionPropagation)

Sets the method for determining Animator start delays.

public TransitionsetStartDelay(long startDelay)

Sets the startDelay of this transition.

from Transitionanimate, 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.Objectequals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Fields

public static final int ORDERING_TOGETHER

A flag used to indicate that the child transitions of this set should all start at the same time.

public static final int ORDERING_SEQUENTIAL

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.

Constructors

public TransitionSet()

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

public TransitionSet setOrdering(int ordering)

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.

public int getOrdering()

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)

public TransitionSet addTransition(Transition transition)

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)

public Transition getTransitionAt(int index)

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()

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.

Parameters:

duration: The length of the animation, in milliseconds.

Returns:

This transitionSet object.

public Transition setStartDelay(long startDelay)

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.

public Transition addTarget(View target)

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

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.

Parameters:

listener: the listener to be added to the current set of listeners for this animation.

Returns:

This transition object.

public Transition removeTarget(View target)

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 removeTarget(View target)

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

public Transition removeListener(Transition.TransitionListener listener)

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.

public void setPathMotion(PathMotion pathMotion)

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

public TransitionSet removeTransition(Transition transition)

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).

public abstract void captureStartValues(TransitionValues transitionValues)

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)

public abstract void captureEndValues(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:

protected void cancel()

This method cancels a transition that is currently running.

public void setPropagation(TransitionPropagation transitionPropagation)

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.

public void setEpicenterCallback(Transition.EpicenterCallback epicenterCallback)

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.

public Transition clone()

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>
 *     &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
 *             android:transitionOrdering="sequential"&gt;
 *         &lt;fade/&gt;
 *         &lt;changeBounds/&gt;
 *     &lt;/transitionSet&gt;
 * </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;
    }

}