Merge "LayoutLib: Animation support in insert/move/removeChild actions."
This commit is contained in:
committed by
Android (Google) Code Review
commit
ad2ff860bf
@@ -335,7 +335,7 @@ public final class Bridge extends LayoutBridge {
|
||||
t2 = t.getCause();
|
||||
}
|
||||
return new BridgeLayoutScene(null,
|
||||
new SceneResult(SceneStatus.ERROR_UNKNOWN, t2.getMessage(), t2));
|
||||
SceneStatus.ERROR_UNKNOWN.getResult(t2.getMessage(), t2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.android.layoutlib.api.LayoutScene.IAnimationListener;
|
||||
import com.android.layoutlib.api.SceneResult.SceneStatus;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.os.Handler;
|
||||
import android.os.Handler_Delegate;
|
||||
@@ -32,7 +31,19 @@ import android.os.Handler_Delegate.IHandlerCallback;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
public class AnimationThread extends Thread {
|
||||
/**
|
||||
* Abstract animation thread.
|
||||
* <p/>
|
||||
* This does not actually start an animation, instead it fakes a looper that will play whatever
|
||||
* animation is sending messages to its own {@link Handler}.
|
||||
* <p/>
|
||||
* Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
|
||||
* <p/>
|
||||
* If {@link #preAnimation()} does not start an animation something then the thread doesn't do
|
||||
* anything.
|
||||
*
|
||||
*/
|
||||
public abstract class AnimationThread extends Thread {
|
||||
|
||||
private static class MessageBundle {
|
||||
final Handler mTarget;
|
||||
@@ -47,17 +58,19 @@ public class AnimationThread extends Thread {
|
||||
}
|
||||
|
||||
private final LayoutSceneImpl mScene;
|
||||
private final Animator mAnimator;
|
||||
|
||||
Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>();
|
||||
private final IAnimationListener mListener;
|
||||
|
||||
public AnimationThread(LayoutSceneImpl scene, Animator animator, IAnimationListener listener) {
|
||||
public AnimationThread(LayoutSceneImpl scene, String threadName, IAnimationListener listener) {
|
||||
super(threadName);
|
||||
mScene = scene;
|
||||
mAnimator = animator;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public abstract SceneResult preAnimation();
|
||||
public abstract void postAnimation();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Bridge.prepareThread();
|
||||
@@ -73,13 +86,20 @@ public class AnimationThread extends Thread {
|
||||
}
|
||||
});
|
||||
|
||||
// start the animation. This will send a message to the handler right away, so
|
||||
// mQueue is filled when this method returns.
|
||||
mAnimator.start();
|
||||
// call out to the pre-animation work, which should start an animation or more.
|
||||
SceneResult result = preAnimation();
|
||||
if (result.isSuccess() == false) {
|
||||
mListener.done(result);
|
||||
}
|
||||
|
||||
// loop the animation
|
||||
LayoutScene scene = mScene.getScene();
|
||||
do {
|
||||
// check early.
|
||||
if (mListener.isCanceled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// get the next message.
|
||||
MessageBundle bundle = mQueue.poll();
|
||||
if (bundle == null) {
|
||||
@@ -97,8 +117,13 @@ public class AnimationThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
// check after sleeping.
|
||||
if (mListener.isCanceled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// ready to do the work, acquire the scene.
|
||||
SceneResult result = mScene.acquire(250);
|
||||
result = mScene.acquire(250);
|
||||
if (result.isSuccess() == false) {
|
||||
mListener.done(result);
|
||||
return;
|
||||
@@ -107,6 +132,11 @@ public class AnimationThread extends Thread {
|
||||
// process the bundle. If the animation is not finished, this will enqueue
|
||||
// the next message, so mQueue will have another one.
|
||||
try {
|
||||
// check after acquiring in case it took a while.
|
||||
if (mListener.isCanceled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
bundle.mTarget.handleMessage(bundle.mMessage);
|
||||
if (mScene.render().isSuccess()) {
|
||||
mListener.onNewFrame(scene);
|
||||
@@ -114,10 +144,11 @@ public class AnimationThread extends Thread {
|
||||
} finally {
|
||||
mScene.release();
|
||||
}
|
||||
} while (mQueue.size() > 0);
|
||||
} while (mListener.isCanceled() == false && mQueue.size() > 0);
|
||||
|
||||
mListener.done(SceneStatus.SUCCESS.getResult());
|
||||
} finally {
|
||||
postAnimation();
|
||||
Handler_Delegate.setCallback(null);
|
||||
Bridge.cleanupThread();
|
||||
}
|
||||
|
||||
@@ -43,8 +43,9 @@ import com.android.layoutlib.bridge.android.BridgeWindow;
|
||||
import com.android.layoutlib.bridge.android.BridgeWindowSession;
|
||||
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorInflater;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.LayoutTransition;
|
||||
import android.app.Fragment_Delegate;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap_Delegate;
|
||||
@@ -56,7 +57,6 @@ import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.View.AttachInfo;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
@@ -346,7 +346,7 @@ public class LayoutSceneImpl {
|
||||
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
} catch (PostInflateException e) {
|
||||
return new SceneResult(SceneStatus.ERROR_INFLATION, e.getMessage(), e);
|
||||
return SceneStatus.ERROR_INFLATION.getResult(e.getMessage(), e);
|
||||
} catch (Throwable e) {
|
||||
// get the real cause of the exception.
|
||||
Throwable t = e;
|
||||
@@ -357,7 +357,7 @@ public class LayoutSceneImpl {
|
||||
// log it
|
||||
mParams.getLogger().error(t);
|
||||
|
||||
return new SceneResult(SceneStatus.ERROR_INFLATION, t.getMessage(), t);
|
||||
return SceneStatus.ERROR_INFLATION.getResult(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,13 +370,14 @@ public class LayoutSceneImpl {
|
||||
* the scene, or if {@link #acquire(long)} was not called.
|
||||
*
|
||||
* @see SceneParams#getRenderingMode()
|
||||
* @see LayoutScene#render(long)
|
||||
*/
|
||||
public SceneResult render() {
|
||||
checkLock();
|
||||
|
||||
try {
|
||||
if (mViewRoot == null) {
|
||||
return new SceneResult(SceneStatus.ERROR_NOT_INFLATED);
|
||||
return SceneStatus.ERROR_NOT_INFLATED.getResult();
|
||||
}
|
||||
// measure the views
|
||||
int w_spec, h_spec;
|
||||
@@ -482,7 +483,7 @@ public class LayoutSceneImpl {
|
||||
// log it
|
||||
mParams.getLogger().error(t);
|
||||
|
||||
return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t);
|
||||
return SceneStatus.ERROR_UNKNOWN.getResult(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,6 +494,8 @@ public class LayoutSceneImpl {
|
||||
*
|
||||
* @throws IllegalStateException if the current context is different than the one owned by
|
||||
* the scene, or if {@link #acquire(long)} was not called.
|
||||
*
|
||||
* @see LayoutScene#animate(Object, String, boolean, IAnimationListener)
|
||||
*/
|
||||
public SceneResult animate(Object targetObject, String animationName,
|
||||
boolean isFrameworkAnimation, IAnimationListener listener) {
|
||||
@@ -515,12 +518,11 @@ public class LayoutSceneImpl {
|
||||
|
||||
if (animationResource != null) {
|
||||
try {
|
||||
ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(
|
||||
mContext, animationId);
|
||||
Animator anim = AnimatorInflater.loadAnimator(mContext, animationId);
|
||||
if (anim != null) {
|
||||
anim.setTarget(targetObject);
|
||||
|
||||
new AnimationThread(this, anim, listener).start();
|
||||
new PlayAnimationThread(anim, this, animationName, listener).start();
|
||||
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
}
|
||||
@@ -531,15 +533,25 @@ public class LayoutSceneImpl {
|
||||
t = t.getCause();
|
||||
}
|
||||
|
||||
return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t);
|
||||
return SceneStatus.ERROR_UNKNOWN.getResult(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
return new SceneResult(SceneStatus.ERROR_ANIM_NOT_FOUND);
|
||||
return SceneStatus.ERROR_ANIM_NOT_FOUND.getResult();
|
||||
}
|
||||
|
||||
public SceneResult insertChild(ViewGroup parentView, IXmlPullParser childXml,
|
||||
int index, IAnimationListener listener) {
|
||||
/**
|
||||
* Insert a new child into an existing parent.
|
||||
* <p>
|
||||
* {@link #acquire(long)} must have been called before this.
|
||||
*
|
||||
* @throws IllegalStateException if the current context is different than the one owned by
|
||||
* the scene, or if {@link #acquire(long)} was not called.
|
||||
*
|
||||
* @see LayoutScene#insertChild(Object, IXmlPullParser, int, IAnimationListener)
|
||||
*/
|
||||
public SceneResult insertChild(final ViewGroup parentView, IXmlPullParser childXml,
|
||||
final int index, IAnimationListener listener) {
|
||||
checkLock();
|
||||
|
||||
// create a block parser for the XML
|
||||
@@ -549,83 +561,216 @@ public class LayoutSceneImpl {
|
||||
// inflate the child without adding it to the root since we want to control where it'll
|
||||
// get added. We do pass the parentView however to ensure that the layoutParams will
|
||||
// be created correctly.
|
||||
View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
|
||||
|
||||
// add it to the parentView in the correct location
|
||||
try {
|
||||
parentView.addView(child, index);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// looks like this is a view class that doesn't support children manipulation!
|
||||
return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
|
||||
}
|
||||
final View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
|
||||
|
||||
invalidateRenderingSize();
|
||||
|
||||
SceneResult result = render();
|
||||
if (listener != null) {
|
||||
new AnimationThread(this, "insertChild", listener) {
|
||||
|
||||
@Override
|
||||
public SceneResult preAnimation() {
|
||||
parentView.setLayoutTransition(new LayoutTransition());
|
||||
return addView(parentView, child, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAnimation() {
|
||||
parentView.setLayoutTransition(null);
|
||||
}
|
||||
}.start();
|
||||
|
||||
// always return success since the real status will come through the listener.
|
||||
return SceneStatus.SUCCESS.getResult(child);
|
||||
}
|
||||
|
||||
// add it to the parentView in the correct location
|
||||
SceneResult result = addView(parentView, child, index);
|
||||
if (result.isSuccess() == false) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = render();
|
||||
if (result.isSuccess()) {
|
||||
result.setData(child);
|
||||
result = result.getCopyWithData(child);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public SceneResult moveChild(ViewGroup parentView, View childView, int index,
|
||||
/**
|
||||
* Adds a given view to a given parent at a given index.
|
||||
*
|
||||
* @param parent the parent to receive the view
|
||||
* @param view the view to add to the parent
|
||||
* @param index the index where to do the add.
|
||||
*
|
||||
* @return a SceneResult with {@link SceneStatus#SUCCESS} or
|
||||
* {@link SceneStatus#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
|
||||
* adding views.
|
||||
*/
|
||||
private SceneResult addView(ViewGroup parent, View view, int index) {
|
||||
try {
|
||||
parent.addView(view, index);
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// looks like this is a view class that doesn't support children manipulation!
|
||||
return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a view to a new parent at a given location
|
||||
* <p>
|
||||
* {@link #acquire(long)} must have been called before this.
|
||||
*
|
||||
* @throws IllegalStateException if the current context is different than the one owned by
|
||||
* the scene, or if {@link #acquire(long)} was not called.
|
||||
*
|
||||
* @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener)
|
||||
*/
|
||||
public SceneResult moveChild(final ViewGroup parentView, final View childView, final int index,
|
||||
Map<String, String> layoutParamsMap, IAnimationListener listener) {
|
||||
checkLock();
|
||||
|
||||
LayoutParams layoutParams = null;
|
||||
try {
|
||||
ViewParent parent = childView.getParent();
|
||||
if (parent instanceof ViewGroup) {
|
||||
ViewGroup parentGroup = (ViewGroup) parent;
|
||||
parentGroup.removeView(childView);
|
||||
}
|
||||
|
||||
// add it to the parentView in the correct location
|
||||
|
||||
if (layoutParamsMap != null) {
|
||||
// need to create a new LayoutParams object for the new parent.
|
||||
layoutParams = parentView.generateLayoutParams(
|
||||
new BridgeLayoutParamsMapAttributes(layoutParamsMap));
|
||||
|
||||
parentView.addView(childView, index, layoutParams);
|
||||
} else {
|
||||
parentView.addView(childView, index);
|
||||
}
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// looks like this is a view class that doesn't support children manipulation!
|
||||
return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
|
||||
}
|
||||
|
||||
invalidateRenderingSize();
|
||||
|
||||
SceneResult result = render();
|
||||
LayoutParams layoutParams = null;
|
||||
if (layoutParamsMap != null) {
|
||||
// need to create a new LayoutParams object for the new parent.
|
||||
layoutParams = parentView.generateLayoutParams(
|
||||
new BridgeLayoutParamsMapAttributes(layoutParamsMap));
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
final LayoutParams params = layoutParams;
|
||||
new AnimationThread(this, "moveChild", listener) {
|
||||
|
||||
@Override
|
||||
public SceneResult preAnimation() {
|
||||
parentView.setLayoutTransition(new LayoutTransition());
|
||||
return moveView(parentView, childView, index, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAnimation() {
|
||||
parentView.setLayoutTransition(null);
|
||||
}
|
||||
}.start();
|
||||
|
||||
// always return success since the real status will come through the listener.
|
||||
return SceneStatus.SUCCESS.getResult(layoutParams);
|
||||
}
|
||||
|
||||
SceneResult result = moveView(parentView, childView, index, layoutParams);
|
||||
if (result.isSuccess() == false) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = render();
|
||||
if (layoutParams != null && result.isSuccess()) {
|
||||
result.setData(layoutParams);
|
||||
result = result.getCopyWithData(layoutParams);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public SceneResult removeChild(View childView, IAnimationListener listener) {
|
||||
checkLock();
|
||||
|
||||
/**
|
||||
* Moves a View from its current parent to a new given parent at a new given location, with
|
||||
* an optional new {@link LayoutParams} instance
|
||||
*
|
||||
* @param parent the new parent
|
||||
* @param view the view to move
|
||||
* @param index the new location in the new parent
|
||||
* @param params an option (can be null) {@link LayoutParams} instance.
|
||||
*
|
||||
* @return a SceneResult with {@link SceneStatus#SUCCESS} or
|
||||
* {@link SceneStatus#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
|
||||
* adding views.
|
||||
*/
|
||||
private SceneResult moveView(ViewGroup parent, View view, int index, LayoutParams params) {
|
||||
try {
|
||||
ViewParent parent = childView.getParent();
|
||||
if (parent instanceof ViewGroup) {
|
||||
ViewGroup parentGroup = (ViewGroup) parent;
|
||||
parentGroup.removeView(childView);
|
||||
ViewGroup previousParent = (ViewGroup) view.getParent();
|
||||
previousParent.removeView(view);
|
||||
|
||||
// add it to the parentView in the correct location
|
||||
|
||||
if (params != null) {
|
||||
parent.addView(view, index, params);
|
||||
} else {
|
||||
parent.addView(view, index);
|
||||
}
|
||||
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// looks like this is a view class that doesn't support children manipulation!
|
||||
return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
|
||||
return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child from its current parent.
|
||||
* <p>
|
||||
* {@link #acquire(long)} must have been called before this.
|
||||
*
|
||||
* @throws IllegalStateException if the current context is different than the one owned by
|
||||
* the scene, or if {@link #acquire(long)} was not called.
|
||||
*
|
||||
* @see LayoutScene#removeChild(Object, IAnimationListener)
|
||||
*/
|
||||
public SceneResult removeChild(final View childView, IAnimationListener listener) {
|
||||
checkLock();
|
||||
|
||||
invalidateRenderingSize();
|
||||
|
||||
final ViewGroup parent = (ViewGroup) childView.getParent();
|
||||
|
||||
if (listener != null) {
|
||||
new AnimationThread(this, "moveChild", listener) {
|
||||
|
||||
@Override
|
||||
public SceneResult preAnimation() {
|
||||
parent.setLayoutTransition(new LayoutTransition());
|
||||
return removeView(parent, childView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAnimation() {
|
||||
parent.setLayoutTransition(null);
|
||||
}
|
||||
}.start();
|
||||
|
||||
// always return success since the real status will come through the listener.
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
}
|
||||
|
||||
SceneResult result = removeView(parent, childView);
|
||||
if (result.isSuccess() == false) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given view from its current parent.
|
||||
*
|
||||
* @param view the view to remove from its parent
|
||||
*
|
||||
* @return a SceneResult with {@link SceneStatus#SUCCESS} or
|
||||
* {@link SceneStatus#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
|
||||
* adding views.
|
||||
*/
|
||||
private SceneResult removeView(ViewGroup parent, View view) {
|
||||
try {
|
||||
parent.removeView(view);
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// looks like this is a view class that doesn't support children manipulation!
|
||||
return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the lock is owned by the current thread and that the current context is the one
|
||||
* from this scene.
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.android.layoutlib.bridge.impl;
|
||||
|
||||
import com.android.layoutlib.api.SceneResult;
|
||||
import com.android.layoutlib.api.LayoutScene.IAnimationListener;
|
||||
import com.android.layoutlib.api.SceneResult.SceneStatus;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
||||
public class PlayAnimationThread extends AnimationThread {
|
||||
|
||||
private final Animator mAnimator;
|
||||
|
||||
public PlayAnimationThread(Animator animator, LayoutSceneImpl scene, String animName,
|
||||
IAnimationListener listener) {
|
||||
super(scene, animName, listener);
|
||||
mAnimator = animator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneResult preAnimation() {
|
||||
// start the animation. This will send a message to the handler right away, so
|
||||
// the queue is filled when this method returns.
|
||||
mAnimator.start();
|
||||
|
||||
return SceneStatus.SUCCESS.getResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAnimation() {
|
||||
// nothing to be done.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user