Merge "LayoutLib: Fix moveChild animation." into honeycomb

This commit is contained in:
Xavier Ducrohet
2011-01-10 10:53:37 -08:00
committed by Android (Google) Code Review
3 changed files with 97 additions and 56 deletions

View File

@@ -195,7 +195,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.RENDER, Capability.RENDER,
Capability.EMBEDDED_LAYOUT, Capability.EMBEDDED_LAYOUT,
Capability.VIEW_MANIPULATION, Capability.VIEW_MANIPULATION,
Capability.ANIMATE); Capability.PLAY_ANIMATION,
Capability.ANIMATED_VIEW_MANIPULATION);
BridgeAssetManager.initSystem(); BridgeAssetManager.initSystem();

View File

@@ -28,7 +28,7 @@ import android.os.Handler_Delegate;
import android.os.Message; import android.os.Message;
import android.os.Handler_Delegate.IHandlerCallback; import android.os.Handler_Delegate.IHandlerCallback;
import java.util.LinkedList; import java.util.PriorityQueue;
import java.util.Queue; import java.util.Queue;
/** /**
@@ -45,7 +45,7 @@ import java.util.Queue;
*/ */
public abstract class AnimationThread extends Thread { public abstract class AnimationThread extends Thread {
private static class MessageBundle { private static class MessageBundle implements Comparable<MessageBundle> {
final Handler mTarget; final Handler mTarget;
final Message mMessage; final Message mMessage;
final long mUptimeMillis; final long mUptimeMillis;
@@ -55,11 +55,18 @@ public abstract class AnimationThread extends Thread {
mMessage = message; mMessage = message;
mUptimeMillis = uptimeMillis; mUptimeMillis = uptimeMillis;
} }
public int compareTo(MessageBundle bundle) {
if (mUptimeMillis < bundle.mUptimeMillis) {
return -1;
}
return 1;
}
} }
private final RenderSessionImpl mSession; private final RenderSessionImpl mSession;
private Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>(); private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
private final IAnimationListener mListener; private final IAnimationListener mListener;
public AnimationThread(RenderSessionImpl scene, String threadName, public AnimationThread(RenderSessionImpl scene, String threadName,

View File

@@ -630,7 +630,7 @@ public class RenderSessionImpl {
* @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener) * @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener)
*/ */
public Result moveChild(final ViewGroup newParentView, final View childView, final int index, public Result moveChild(final ViewGroup newParentView, final View childView, final int index,
Map<String, String> layoutParamsMap, IAnimationListener listener) { Map<String, String> layoutParamsMap, final IAnimationListener listener) {
checkLock(); checkLock();
invalidateRenderingSize(); invalidateRenderingSize();
@@ -647,42 +647,73 @@ public class RenderSessionImpl {
if (listener != null) { if (listener != null) {
final LayoutParams params = layoutParams; final LayoutParams params = layoutParams;
new AnimationThread(this, "moveChild", listener) {
@Override // there is no support for animating views across layouts, so in case the new and old
public Result preAnimation() { // parent views are different we fake the animation through a no animation thread.
// set up the transition for the previous parent. if (previousParent != newParentView) {
LayoutTransition removeTransition = new LayoutTransition(); new Thread("not animated moveChild") {
previousParent.setLayoutTransition(removeTransition); @Override
public void run() {
Result result = moveView(previousParent, newParentView, childView, index,
params);
if (result.isSuccess() == false) {
listener.done(result);
}
// no fade-out. Because we can't rely on layout transition listeners when // ready to do the work, acquire the scene.
// there is no Animator at all, instead we keep the animator but set its result = acquire(250);
// duration to 0. if (result.isSuccess() == false) {
// Note: Cannot user Animation.setDuration() directly. Have to set it listener.done(result);
// on the LayoutTransition. return;
removeTransition.setDuration(LayoutTransition.DISAPPEARING, 0); }
if (previousParent != newParentView) { try {
// different parent, set a Layout transition on the new parent. result = render();
newParentView.setLayoutTransition(new LayoutTransition()); if (result.isSuccess()) {
listener.onNewFrame(RenderSessionImpl.this.getSession());
}
} finally {
release();
}
listener.done(result);
}
}.start();
} else {
new AnimationThread(this, "moveChild", listener) {
@Override
public Result preAnimation() {
// set up the transition for the parent.
LayoutTransition transition = new LayoutTransition();
previousParent.setLayoutTransition(transition);
// tweak the animation durations and start delays (to match the duration of
// animation playing just before).
// Note: Cannot user Animation.setDuration() directly. Have to set it
// on the LayoutTransition.
transition.setDuration(LayoutTransition.DISAPPEARING, 100);
// CHANGE_DISAPPEARING plays after DISAPPEARING
transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100);
transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 100);
transition.setDuration(LayoutTransition.CHANGE_APPEARING, 100);
// CHANGE_APPEARING plays after CHANGE_APPEARING
transition.setStartDelay(LayoutTransition.APPEARING, 100);
transition.setDuration(LayoutTransition.APPEARING, 100);
return moveView(previousParent, newParentView, childView, index, params);
} }
// no fade-in. Because we can't rely on layout transition listeners when @Override
// there is no Animator at all, instead we keep the animator but set its public void postAnimation() {
// duration to 0. previousParent.setLayoutTransition(null);
// Note: Cannot user Animation.setDuration() directly. Have to set it newParentView.setLayoutTransition(null);
// on the LayoutTransition. }
newParentView.getLayoutTransition().setDuration(LayoutTransition.APPEARING, 0); }.start();
}
return moveView(previousParent, newParentView, childView, index, params);
}
@Override
public void postAnimation() {
previousParent.setLayoutTransition(null);
newParentView.setLayoutTransition(null);
}
}.start();
// always return success since the real status will come through the listener. // always return success since the real status will come through the listener.
return SUCCESS.createResult(layoutParams); return SUCCESS.createResult(layoutParams);
@@ -707,7 +738,7 @@ public class RenderSessionImpl {
* *
* @param previousParent the previous parent, still owning the child at the time of the call. * @param previousParent the previous parent, still owning the child at the time of the call.
* @param newParent the new parent * @param newParent the new parent
* @param view the view to move * @param movedView the view to move
* @param index the new location in the new parent * @param index the new location in the new parent
* @param params an option (can be null) {@link LayoutParams} instance. * @param params an option (can be null) {@link LayoutParams} instance.
* *
@@ -715,12 +746,12 @@ public class RenderSessionImpl {
* {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support * {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
* adding views. * adding views.
*/ */
private Result moveView(ViewGroup previousParent, final ViewGroup newParent, View view, private Result moveView(ViewGroup previousParent, final ViewGroup newParent,
final int index, final LayoutParams params) { final View movedView, final int index, final LayoutParams params) {
try { try {
// check if there is a transition on the previousParent. // check if there is a transition on the previousParent.
LayoutTransition transition = previousParent.getLayoutTransition(); LayoutTransition previousTransition = previousParent.getLayoutTransition();
if (transition != null) { if (previousTransition != null) {
// in this case there is an animation. This means we have to wait for the child's // in this case there is an animation. This means we have to wait for the child's
// parent reference to be null'ed out so that we can add it to the new parent. // parent reference to be null'ed out so that we can add it to the new parent.
// It is technically removed right before the DISAPPEARING animation is done (if // It is technically removed right before the DISAPPEARING animation is done (if
@@ -730,48 +761,50 @@ public class RenderSessionImpl {
// parent, we need to wait until the CHANGE_DISAPPEARING animation is done before // parent, we need to wait until the CHANGE_DISAPPEARING animation is done before
// adding the child or the child will appear in its new location before the // adding the child or the child will appear in its new location before the
// other children have made room for it. // other children have made room for it.
// If the parents are different, then we can add the child to its new parent right
// after the DISAPPEARING animation is done.
final int waitForType = newParent == previousParent ?
LayoutTransition.CHANGE_DISAPPEARING : LayoutTransition.DISAPPEARING;
// add a listener to the transition to be notified of the actual removal. // add a listener to the transition to be notified of the actual removal.
transition.addTransitionListener(new TransitionListener() { previousTransition.addTransitionListener(new TransitionListener() {
private int mChangeDisappearingCount = 0;
public void startTransition(LayoutTransition transition, ViewGroup container, public void startTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) { View view, int transitionType) {
// don't care. if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
mChangeDisappearingCount++;
}
} }
public void endTransition(LayoutTransition transition, ViewGroup container, public void endTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) { View view, int transitionType) {
if (transitionType == waitForType) { if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
mChangeDisappearingCount--;
}
if (transitionType == LayoutTransition.CHANGE_DISAPPEARING &&
mChangeDisappearingCount == 0) {
// add it to the parentView in the correct location // add it to the parentView in the correct location
if (params != null) { if (params != null) {
newParent.addView(view, index, params); newParent.addView(movedView, index, params);
} else { } else {
newParent.addView(view, index); newParent.addView(movedView, index);
} }
} }
} }
}); });
// remove the view from the current parent. // remove the view from the current parent.
previousParent.removeView(view); previousParent.removeView(movedView);
// and return since adding the view to the new parent is done in the listener. // and return since adding the view to the new parent is done in the listener.
return SUCCESS.createResult(); return SUCCESS.createResult();
} else { } else {
// standard code with no animation. pretty simple. // standard code with no animation. pretty simple.
previousParent.removeView(view); previousParent.removeView(movedView);
// add it to the parentView in the correct location // add it to the parentView in the correct location
if (params != null) { if (params != null) {
newParent.addView(view, index, params); newParent.addView(movedView, index, params);
} else { } else {
newParent.addView(view, index); newParent.addView(movedView, index);
} }
return SUCCESS.createResult(); return SUCCESS.createResult();