LayoutLib: Fix moveChild animation.
Only support animation for view move inside
the same layout.
Also fix animation thread to property support multiple
animation running at once.
Change-Id: I45cad84e7b9f3a4f281c956d32934eb74e807afb
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index e1bf925..c042327 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -195,7 +195,8 @@
Capability.RENDER,
Capability.EMBEDDED_LAYOUT,
Capability.VIEW_MANIPULATION,
- Capability.ANIMATE);
+ Capability.PLAY_ANIMATION,
+ Capability.ANIMATED_VIEW_MANIPULATION);
BridgeAssetManager.initSystem();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index 125c1e6..033e065 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -28,7 +28,7 @@
import android.os.Message;
import android.os.Handler_Delegate.IHandlerCallback;
-import java.util.LinkedList;
+import java.util.PriorityQueue;
import java.util.Queue;
/**
@@ -45,7 +45,7 @@
*/
public abstract class AnimationThread extends Thread {
- private static class MessageBundle {
+ private static class MessageBundle implements Comparable<MessageBundle> {
final Handler mTarget;
final Message mMessage;
final long mUptimeMillis;
@@ -55,11 +55,18 @@
mMessage = message;
mUptimeMillis = uptimeMillis;
}
+
+ public int compareTo(MessageBundle bundle) {
+ if (mUptimeMillis < bundle.mUptimeMillis) {
+ return -1;
+ }
+ return 1;
+ }
}
private final RenderSessionImpl mSession;
- private Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>();
+ private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
private final IAnimationListener mListener;
public AnimationThread(RenderSessionImpl scene, String threadName,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 025a318..55a5bc0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -630,7 +630,7 @@
* @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener)
*/
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();
invalidateRenderingSize();
@@ -647,42 +647,73 @@
if (listener != null) {
final LayoutParams params = layoutParams;
- new AnimationThread(this, "moveChild", listener) {
- @Override
- public Result preAnimation() {
- // set up the transition for the previous parent.
- LayoutTransition removeTransition = new LayoutTransition();
- previousParent.setLayoutTransition(removeTransition);
+ // there is no support for animating views across layouts, so in case the new and old
+ // parent views are different we fake the animation through a no animation thread.
+ if (previousParent != newParentView) {
+ new Thread("not animated moveChild") {
+ @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
- // there is no Animator at all, instead we keep the animator but set its
- // duration to 0.
- // Note: Cannot user Animation.setDuration() directly. Have to set it
- // on the LayoutTransition.
- removeTransition.setDuration(LayoutTransition.DISAPPEARING, 0);
+ // ready to do the work, acquire the scene.
+ result = acquire(250);
+ if (result.isSuccess() == false) {
+ listener.done(result);
+ return;
+ }
- if (previousParent != newParentView) {
- // different parent, set a Layout transition on the new parent.
- newParentView.setLayoutTransition(new LayoutTransition());
+ try {
+ result = render();
+ 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
- // there is no Animator at all, instead we keep the animator but set its
- // duration to 0.
- // Note: Cannot user Animation.setDuration() directly. Have to set it
- // on the LayoutTransition.
- newParentView.getLayoutTransition().setDuration(LayoutTransition.APPEARING, 0);
-
- return moveView(previousParent, newParentView, childView, index, params);
- }
-
- @Override
- public void postAnimation() {
- previousParent.setLayoutTransition(null);
- newParentView.setLayoutTransition(null);
- }
- }.start();
+ @Override
+ public void postAnimation() {
+ previousParent.setLayoutTransition(null);
+ newParentView.setLayoutTransition(null);
+ }
+ }.start();
+ }
// always return success since the real status will come through the listener.
return SUCCESS.createResult(layoutParams);
@@ -707,7 +738,7 @@
*
* @param previousParent the previous parent, still owning the child at the time of the call.
* @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 params an option (can be null) {@link LayoutParams} instance.
*
@@ -715,12 +746,12 @@
* {@link Status#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
* adding views.
*/
- private Result moveView(ViewGroup previousParent, final ViewGroup newParent, View view,
- final int index, final LayoutParams params) {
+ private Result moveView(ViewGroup previousParent, final ViewGroup newParent,
+ final View movedView, final int index, final LayoutParams params) {
try {
// check if there is a transition on the previousParent.
- LayoutTransition transition = previousParent.getLayoutTransition();
- if (transition != null) {
+ LayoutTransition previousTransition = previousParent.getLayoutTransition();
+ if (previousTransition != null) {
// 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.
// It is technically removed right before the DISAPPEARING animation is done (if
@@ -730,48 +761,50 @@
// 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
// 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.
- transition.addTransitionListener(new TransitionListener() {
+ previousTransition.addTransitionListener(new TransitionListener() {
+ private int mChangeDisappearingCount = 0;
public void startTransition(LayoutTransition transition, ViewGroup container,
View view, int transitionType) {
- // don't care.
+ if (transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
+ mChangeDisappearingCount++;
+ }
}
public void endTransition(LayoutTransition transition, ViewGroup container,
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
if (params != null) {
- newParent.addView(view, index, params);
+ newParent.addView(movedView, index, params);
} else {
- newParent.addView(view, index);
+ newParent.addView(movedView, index);
}
-
}
}
});
// 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.
return SUCCESS.createResult();
} else {
// standard code with no animation. pretty simple.
- previousParent.removeView(view);
+ previousParent.removeView(movedView);
// add it to the parentView in the correct location
if (params != null) {
- newParent.addView(view, index, params);
+ newParent.addView(movedView, index, params);
} else {
- newParent.addView(view, index);
+ newParent.addView(movedView, index);
}
return SUCCESS.createResult();