blob: 313e33e601bdfe4c47f56923b6e88ac132c29b91 [file] [log] [blame]
/*
* 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.view.transition;
import android.util.AndroidRuntimeException;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* A TransitionGroup is a parent of child transitions (including other
* TransitionGroups). Using TransitionGroups enables more complex
* choreography of transitions, where some groups play {@link #TOGETHER} and
* others play {@link #SEQUENTIALLY}. For example, {@link AutoTransition}
* uses a TransitionGroup to sequentially play a Fade(Fade.OUT), followed by
* a {@link Move}, followed by a Fade(Fade.OUT) transition.
*/
public class TransitionGroup 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 group
* should all start at the same time.
*/
public static final int TOGETHER = 0;
/**
* A flag used to indicate that the child transitions of this group 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 SEQUENTIALLY = 1;
/**
* Constructs an empty transition group. Add child transitions to the
* group by calling to {@link #addTransitions(Transition...)} )}. By default,
* child transitions will play {@link #TOGETHER}.
*/
public TransitionGroup() {
}
/**
* Constructs an empty transition group with the specified ordering.
*
* @param ordering {@link #TOGETHER} to start this group's child
* transitions together, {@link #SEQUENTIALLY} to play the child
* transitions in sequence.
* @see #setOrdering(int)
*/
public TransitionGroup(int ordering) {
setOrdering(ordering);
}
/**
* Sets the play order of this group's child transitions.
*
* @param ordering {@link #TOGETHER} to start this group's child
* transitions together, {@link #SEQUENTIALLY} to play the child
* transitions in sequence.
*/
public void setOrdering(int ordering) {
switch (ordering) {
case SEQUENTIALLY:
mPlayTogether = false;
break;
case TOGETHER:
mPlayTogether = true;
break;
default:
throw new AndroidRuntimeException("Invalid parameter for TransitionGroup " +
"ordering: " + ordering);
}
}
/**
* Adds child transitions to this group. The order of the child transitions
* passed in determines the order in which they are started.
*
* @param transitions A list of child transition to be added to this group.
*/
public void addTransitions(Transition... transitions) {
if (transitions != null) {
int numTransitions = transitions.length;
for (int i = 0; i < numTransitions; ++i) {
mTransitions.add(transitions[i]);
transitions[i].mParent = this;
if (mDuration >= 0) {
transitions[i].setDuration(mDuration);
}
}
}
}
/**
* Setting a non-negative duration on a TransitionGroup causes all of the child
* transitions (current and future) to inherit this duration.
*
* @param duration The length of the animation, in milliseconds.
* @return This transitionGroup object.
*/
@Override
public Transition setDuration(long duration) {
super.setDuration(duration);
if (mDuration >= 0) {
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).setDuration(duration);
}
}
return this;
}
/**
* Removes the specified child transition from this group.
*
* @param transition The transition to be removed.
*/
public void removeTransition(Transition transition) {
mTransitions.remove(transition);
transition.mParent = null;
}
/**
* Sets up listeners for each of the child transitions. This is used to
* determine when this transition group is finished (all child transitions
* must finish first).
*/
private void setupStartEndListeners() {
TransitionGroupListener listener = new TransitionGroupListener(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 group is also done.
*/
static class TransitionGroupListener extends TransitionListenerAdapter {
TransitionGroup mTransitionGroup;
TransitionGroupListener(TransitionGroup transitionGroup) {
mTransitionGroup = transitionGroup;
}
@Override
public void onTransitionStart(Transition transition) {
if (!mTransitionGroup.mStarted) {
mTransitionGroup.startTransition();
mTransitionGroup.mStarted = true;
}
}
@Override
public void onTransitionEnd(Transition transition) {
--mTransitionGroup.mCurrentListeners;
if (mTransitionGroup.mCurrentListeners == 0) {
// All child trans
mTransitionGroup.mStarted = false;
mTransitionGroup.endTransition();
}
transition.removeListener(this);
}
}
/**
* @hide
*/
@Override
protected void play(ViewGroup sceneRoot, TransitionValuesMaps startValues,
TransitionValuesMaps endValues) {
for (Transition childTransition : mTransitions) {
childTransition.play(sceneRoot, startValues, endValues);
}
}
/**
* @hide
*/
@Override
protected void runAnimations() {
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(Transition transition) {
nextTransition.runAnimations();
transition.removeListener(this);
}
});
}
Transition firstTransition = mTransitions.get(0);
if (firstTransition != null) {
firstTransition.runAnimations();
}
} else {
for (Transition childTransition : mTransitions) {
childTransition.runAnimations();
}
}
}
@Override
protected void captureValues(TransitionValues transitionValues, boolean start) {
int targetId = transitionValues.view.getId();
for (Transition childTransition : mTransitions) {
if (childTransition.isValidTarget(transitionValues.view, targetId)) {
childTransition.captureValues(transitionValues, start);
}
}
}
@Override
protected void cancelTransition() {
super.cancelTransition();
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).cancelTransition();
}
}
@Override
void setSceneRoot(ViewGroup sceneRoot) {
super.setSceneRoot(sceneRoot);
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
mTransitions.get(i).setSceneRoot(sceneRoot);
}
}
@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 TransitionGroup clone() {
TransitionGroup clone = (TransitionGroup) super.clone();
clone.mTransitions = new ArrayList<Transition>();
int numTransitions = mTransitions.size();
for (int i = 0; i < numTransitions; ++i) {
clone.mTransitions.add((Transition) mTransitions.get(i).clone());
}
return clone;
}
}