/*
* Copyright (C) 2013 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 android.transition;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AndroidRuntimeException;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import com.android.internal.R;
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 ORDERING_TOGETHER
and others play 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.TransitionSet
and Transition.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>
/**
* 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 {@link android.R.styleable#TransitionSet} and
* {@link android.R.styleable#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 {
ArrayList<Transition> mTransitions = new ArrayList<Transition>();
private boolean mPlayTogether = true;
int mCurrentListeners;
boolean mStarted = false;
A flag used to indicate that the child transitions of this set
should all start at the same time.
/**
* 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.
/**
* 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 addTransition(Transition)
)}. By default, child transitions will play together
. /**
* 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(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TransitionSet);
int ordering = a.getInt(R.styleable.TransitionSet_transitionOrdering,
TransitionSet.ORDERING_TOGETHER);
setOrdering(ordering);
a.recycle();
}
Sets the play order of this set's child transitions.
Params: - ordering –
ORDERING_TOGETHER
to play this set's child transitions together, ORDERING_SEQUENTIAL
to play the child transitions in sequence.
Returns: This transitionSet object.
/**
* 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.
*/
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 ORDERING_TOGETHER
. See Also: Returns: ORDERING_TOGETHER
if child transitions will play at the same time, ORDERING_SEQUENTIAL
if they will play in sequence.
/**
* 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 ordering
property, determines the order in which the transitions are started. If this transitionSet has a duration
set on it, the child transition will inherit that duration. Transitions are assumed to have a maximum of one transitionSet parent.
Params: - transition – A non-null child transition to be added to this set.
Returns: This transitionSet object.
/**
* 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} set on it, the
* child transition will inherit that duration. 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.
*/
public TransitionSet addTransition(Transition transition) {
if (transition != null) {
mTransitions.add(transition);
transition.mParent = this;
if (mDuration >= 0) {
transition.setDuration(mDuration);
}
}
return this;
}
Returns the number of child transitions in the TransitionSet.
See Also: Returns: The number of child transitions in the TransitionSet.
/**
* 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.
Params: - index – The position of the Transition to retrieve.
See Also:
/**
* 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()
*/
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.
Params: - duration – The length of the animation, in milliseconds.
Returns: This transitionSet object.
/**
* 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.
*/
@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;
}
@Override
public TransitionSet setStartDelay(long startDelay) {
return (TransitionSet) super.setStartDelay(startDelay);
}
@Override
public TransitionSet setInterpolator(TimeInterpolator interpolator) {
return (TransitionSet) super.setInterpolator(interpolator);
}
@Override
public TransitionSet addTarget(View target) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).addTarget(target);
}
return (TransitionSet) super.addTarget(target);
}
@Override
public TransitionSet addTarget(int targetId) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).addTarget(targetId);
}
return (TransitionSet) super.addTarget(targetId);
}
@Override
public TransitionSet addTarget(String targetName) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).addTarget(targetName);
}
return (TransitionSet) super.addTarget(targetName);
}
@Override
public TransitionSet addTarget(Class targetType) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).addTarget(targetType);
}
return (TransitionSet) super.addTarget(targetType);
}
@Override
public TransitionSet addListener(TransitionListener listener) {
return (TransitionSet) super.addListener(listener);
}
@Override
public TransitionSet removeTarget(int targetId) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).removeTarget(targetId);
}
return (TransitionSet) super.removeTarget(targetId);
}
@Override
public TransitionSet removeTarget(View target) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).removeTarget(target);
}
return (TransitionSet) super.removeTarget(target);
}
@Override
public TransitionSet removeTarget(Class target) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).removeTarget(target);
}
return (TransitionSet) super.removeTarget(target);
}
@Override
public TransitionSet removeTarget(String target) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).removeTarget(target);
}
return (TransitionSet) super.removeTarget(target);
}
@Override
public Transition excludeTarget(View target, boolean exclude) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).excludeTarget(target, exclude);
}
return super.excludeTarget(target, exclude);
}
@Override
public Transition excludeTarget(String targetName, boolean exclude) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).excludeTarget(targetName, exclude);
}
return super.excludeTarget(targetName, exclude);
}
@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);
}
@Override
public Transition excludeTarget(Class type, boolean exclude) {
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).excludeTarget(type, exclude);
}
return super.excludeTarget(type, exclude);
}
@Override
public TransitionSet removeListener(TransitionListener listener) {
return (TransitionSet) super.removeListener(listener);
}
@Override
public void setPathMotion(PathMotion pathMotion) {
super.setPathMotion(pathMotion);
for (int i = 0; i < mTransitions.size(); i++) {
mTransitions.get(i).setPathMotion(pathMotion);
}
}
Removes the specified child transition from this set.
Params: - transition – The transition to be removed.
Returns: This transitionSet object.
/**
* Removes the specified child transition from this set.
*
* @param transition The transition to be removed.
* @return This transitionSet object.
*/
public TransitionSet removeTransition(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).
/**
* 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.
/**
* 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(Transition transition) {
if (!mTransitionSet.mStarted) {
mTransitionSet.start();
mTransitionSet.mStarted = true;
}
}
@Override
public void onTransitionEnd(Transition transition) {
--mTransitionSet.mCurrentListeners;
if (mTransitionSet.mCurrentListeners == 0) {
// All child trans
mTransitionSet.mStarted = false;
mTransitionSet.end();
}
transition.removeListener(this);
}
}
@hide
/**
* @hide
*/
@Override
protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
TransitionValuesMaps endValues, ArrayList<TransitionValues> startValuesList,
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);
}
}
@hide
/**
* @hide
*/
@Override
protected void runAnimators() {
if (mTransitions.isEmpty()) {
start();
end();
return;
}
setupStartEndListeners();
int numTransitions = mTransitions.size();
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 < numTransitions; ++i) {
Transition previousTransition = mTransitions.get(i - 1);
final Transition nextTransition = mTransitions.get(i);
previousTransition.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
nextTransition.runAnimators();
transition.removeListener(this);
}
});
}
Transition firstTransition = mTransitions.get(0);
if (firstTransition != null) {
firstTransition.runAnimators();
}
} else {
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).runAnimators();
}
}
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
if (isValidTarget(transitionValues.view)) {
for (Transition childTransition : mTransitions) {
if (childTransition.isValidTarget(transitionValues.view)) {
childTransition.captureStartValues(transitionValues);
transitionValues.targetedTransitions.add(childTransition);
}
}
}
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
if (isValidTarget(transitionValues.view)) {
for (Transition childTransition : mTransitions) {
if (childTransition.isValidTarget(transitionValues.view)) {
childTransition.captureEndValues(transitionValues);
transitionValues.targetedTransitions.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);
}
}
@hide
/** @hide */
@Override
public void pause(View sceneRoot) {
super.pause(sceneRoot);
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).pause(sceneRoot);
}
}
@hide
/** @hide */
@Override
public void resume(View sceneRoot) {
super.resume(sceneRoot);
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).resume(sceneRoot);
}
}
@hide
/** @hide */
@Override
protected void cancel() {
super.cancel();
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).cancel();
}
}
@hide
/** @hide */
@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
TransitionSet setSceneRoot(ViewGroup sceneRoot) {
super.setSceneRoot(sceneRoot);
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).setSceneRoot(sceneRoot);
}
return (TransitionSet) this;
}
@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(TransitionPropagation propagation) {
super.setPropagation(propagation);
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).setPropagation(propagation);
}
}
@Override
public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
super.setEpicenterCallback(epicenterCallback);
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;
}
@Override
public TransitionSet clone() {
TransitionSet clone = (TransitionSet) super.clone();
clone.mTransitions = new ArrayList<Transition>();
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
clone.addTransition((Transition) mTransitions.get(i).clone());
}
return clone;
}
}