diff --git a/Android/doric/src/main/java/pub/doric/DoricContext.java b/Android/doric/src/main/java/pub/doric/DoricContext.java index 09d7b0d2..a324769c 100644 --- a/Android/doric/src/main/java/pub/doric/DoricContext.java +++ b/Android/doric/src/main/java/pub/doric/DoricContext.java @@ -15,6 +15,8 @@ */ package pub.doric; +import android.animation.Animator; +import android.animation.AnimatorSet; import android.content.Context; import com.github.pengfeizhou.jscore.JSDecoder; @@ -193,4 +195,15 @@ public class DoricContext { public IDoricNavBar getDoricNavBar() { return this.doricNavBar; } + + private AnimatorSet animatorSet; + + public void setAnimatorSet(AnimatorSet animatorSet) { + this.animatorSet = animatorSet; + } + + public AnimatorSet getAnimatorSet() { + return this.animatorSet; + } + } diff --git a/Android/doric/src/main/java/pub/doric/DoricRegistry.java b/Android/doric/src/main/java/pub/doric/DoricRegistry.java index 39a1ca5f..75f087e5 100644 --- a/Android/doric/src/main/java/pub/doric/DoricRegistry.java +++ b/Android/doric/src/main/java/pub/doric/DoricRegistry.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import pub.doric.loader.DoricAssetJSLoader; import pub.doric.loader.DoricHttpJSLoader; import pub.doric.loader.IDoricJSLoader; +import pub.doric.plugin.AnimatePlugin; import pub.doric.plugin.NavBarPlugin; import pub.doric.plugin.NavigatorPlugin; import pub.doric.plugin.NetworkPlugin; @@ -89,6 +90,7 @@ public class DoricRegistry { this.registerNativePlugin(NavigatorPlugin.class); this.registerNativePlugin(NavBarPlugin.class); this.registerNativePlugin(PopoverPlugin.class); + this.registerNativePlugin(AnimatePlugin.class); this.registerViewNode(RootNode.class); this.registerViewNode(TextNode.class); diff --git a/Android/doric/src/main/java/pub/doric/plugin/AnimatePlugin.java b/Android/doric/src/main/java/pub/doric/plugin/AnimatePlugin.java new file mode 100644 index 00000000..1f9383fd --- /dev/null +++ b/Android/doric/src/main/java/pub/doric/plugin/AnimatePlugin.java @@ -0,0 +1,86 @@ +package pub.doric.plugin; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.text.TextUtils; + +import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JavaValue; + +import java.util.concurrent.Callable; + +import pub.doric.DoricContext; +import pub.doric.async.AsyncResult; +import pub.doric.extension.bridge.DoricMethod; +import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; +import pub.doric.shader.RootNode; +import pub.doric.shader.ViewNode; +import pub.doric.utils.DoricLog; +import pub.doric.utils.ThreadMode; + +/** + * @Description: pub.doric.plugin + * @Author: pengfei.zhou + * @CreateDate: 2019-11-29 + */ +@DoricPlugin(name = "animate") +public class AnimatePlugin extends DoricJavaPlugin { + public AnimatePlugin(DoricContext doricContext) { + super(doricContext); + } + + @DoricMethod + public void submit(DoricPromise promise) { + promise.resolve(); + } + + @DoricMethod + public void animateRender(final JSObject jsObject, final DoricPromise promise) { + getDoricContext().getDriver().asyncCall(new Callable() { + @Override + public Object call() throws Exception { + final long duration = jsObject.getProperty("duration").asNumber().toLong(); + AnimatorSet animatorSet = new AnimatorSet(); + getDoricContext().setAnimatorSet(animatorSet); + String viewId = jsObject.getProperty("id").asString().value(); + RootNode rootNode = getDoricContext().getRootNode(); + if (TextUtils.isEmpty(rootNode.getId())) { + rootNode.setId(viewId); + rootNode.blend(jsObject.getProperty("props").asObject()); + } else { + ViewNode viewNode = getDoricContext().targetViewNode(viewId); + if (viewNode != null) { + viewNode.blend(jsObject.getProperty("props").asObject()); + } + } + getDoricContext().setAnimatorSet(null); + animatorSet.setDuration(duration); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + promise.resolve(); + } + }); + animatorSet.start(); + return null; + } + }, ThreadMode.UI).setCallback(new AsyncResult.Callback() { + @Override + public void onResult(Object result) { + } + + @Override + public void onError(Throwable t) { + t.printStackTrace(); + DoricLog.e("Shader.render:error%s", t.getLocalizedMessage()); + promise.reject(new JavaValue(t.getLocalizedMessage())); + } + + @Override + public void onFinish() { + } + }); + } +} diff --git a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java index 204291be..19b5c62d 100644 --- a/Android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/Android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -15,6 +15,8 @@ */ package pub.doric.shader; +import android.animation.Animator; +import android.animation.ObjectAnimator; import android.content.Context; import android.view.View; import android.view.ViewGroup; @@ -123,16 +125,48 @@ public abstract class ViewNode extends DoricContextHolder { protected void blend(T view, String name, JSValue prop) { switch (name) { case "width": - setWidth(prop.asNumber().toFloat()); + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getWidth(), + prop.asNumber().toFloat())); + } else { + setWidth(prop.asNumber().toFloat()); + } break; case "height": - setHeight(prop.asNumber().toFloat()); + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getHeight(), + prop.asNumber().toFloat())); + } else { + setHeight(prop.asNumber().toFloat()); + } break; case "x": - setX(prop.asNumber().toFloat()); + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getX(), + prop.asNumber().toFloat())); + } else { + setX(prop.asNumber().toFloat()); + } break; case "y": - setY(prop.asNumber().toFloat()); + if (isAnimating()) { + addAnimator(ObjectAnimator.ofFloat( + this, + name, + getY(), + prop.asNumber().toFloat())); + } else { + setY(prop.asNumber().toFloat()); + } break; case "bgColor": view.setBackgroundColor(prop.asNumber().toInt()); @@ -244,30 +278,6 @@ public abstract class ViewNode extends DoricContextHolder { return mId; } - protected void setWidth(float width) { - if (mLayoutParams.width >= 0) { - mLayoutParams.width = DoricUtils.dp2px(width); - } - } - - protected void setHeight(float height) { - if (mLayoutParams.height >= 0) { - mLayoutParams.height = DoricUtils.dp2px(height); - } - } - - protected void setX(float x) { - if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { - ((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x); - } - } - - protected void setY(float y) { - if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { - ((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y); - } - } - protected void setLayoutConfig(JSObject layoutConfig) { if (mSuperNode != null) { mSuperNode.blendSubLayoutConfig(this, layoutConfig); @@ -329,14 +339,25 @@ public abstract class ViewNode extends DoricContextHolder { } } - @DoricMethod - public int getWidth() { - return getNodeView().getWidth(); + protected boolean isAnimating() { + return getDoricContext().getAnimatorSet() != null; + } + + protected void addAnimator(Animator animator) { + if (getDoricContext().getAnimatorSet() == null) { + return; + } + getDoricContext().getAnimatorSet().play(animator); } @DoricMethod - public int getHeight() { - return getNodeView().getHeight(); + public float getWidth() { + return DoricUtils.px2dp(getNodeView().getWidth()); + } + + @DoricMethod + public float getHeight() { + return DoricUtils.px2dp(getNodeView().getHeight()); } @DoricMethod @@ -355,4 +376,53 @@ public abstract class ViewNode extends DoricContextHolder { public float getRotation() { return getNodeView().getRotation() / 360; } + + @DoricMethod + protected void setWidth(float width) { + if (mLayoutParams.width >= 0) { + mLayoutParams.width = DoricUtils.dp2px(width); + mView.requestLayout(); + } + } + + @DoricMethod + protected void setHeight(float height) { + if (mLayoutParams.height >= 0) { + mLayoutParams.height = DoricUtils.dp2px(height); + mView.requestLayout(); + } + } + + @DoricMethod + protected void setX(float x) { + if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { + ((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x); + mView.requestLayout(); + } + } + + @DoricMethod + protected void setY(float y) { + if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { + ((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y); + mView.requestLayout(); + } + } + + @DoricMethod + public float getX() { + if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { + return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin); + } + return 0; + } + + @DoricMethod + public float getY() { + if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) { + return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin); + } + return 0; + } + } diff --git a/demo/src/AnimatorDemo.ts b/demo/src/AnimatorDemo.ts index fe44d5b2..1acdae9c 100644 --- a/demo/src/AnimatorDemo.ts +++ b/demo/src/AnimatorDemo.ts @@ -5,7 +5,7 @@ import { title, label, colors, box } from "./utils"; class AnimatorDemo extends Panel { build(rootView: Group): void { const view = box(2) - scroller(vlayout([ + vlayout([ title("Animator Demo"), label('Reset').apply({ width: 100, @@ -79,11 +79,9 @@ class AnimatorDemo extends Panel { bgColor: colors[1].alpha(0.3 * 255), }), ]).apply({ - layoutConfig: layoutConfig().atmost().h(LayoutSpec.WRAP_CONTENT), + layoutConfig: layoutConfig().atmost(), gravity: gravity().center(), space: 10, - } as IVLayout)).apply({ - layoutConfig: layoutConfig().atmost(), - }).in(rootView) + } as IVLayout).in(rootView) } } \ No newline at end of file