From 84598dc54a216ff2247b5edeb7d012ff1e683cc4 Mon Sep 17 00:00:00 2001 From: YorkShen Date: Fri, 17 Mar 2017 11:27:11 +0800 Subject: [PATCH 1/2] * [Android] Refactor animation using RenderAction and DomAction Potential break changes for Android developer: Those who start animation by invoking AnimationModule, RenderManager, or DomManager directly may run into compiling error or runtimeException. --- CHANGELOG.md | 4 +- .../taobao/weex/dom/DOMActionContextImpl.java | 62 +-- .../com/taobao/weex/dom/WXDomHandler.java | 9 +- .../com/taobao/weex/dom/WXDomManager.java | 28 +- .../com/taobao/weex/dom/action/Actions.java | 38 +- .../weex/dom/action/AnimationAction.java | 463 ++++++++++++++++++ .../weex/ui/RenderActionContextImpl.java | 4 - .../com/taobao/weex/ui/WXRenderManager.java | 12 - .../ui/animation/DimensionUpdateListener.java | 6 +- .../weex/ui/animation/WXAnimationModule.java | 199 +------- .../taobao/weex/dom/WXDomStatementTest.java | 11 - .../ui/animation/WXAnimationModuleTest.java | 18 +- 12 files changed, 534 insertions(+), 320 deletions(-) create mode 100644 android/sdk/src/main/java/com/taobao/weex/dom/action/AnimationAction.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e44157aacc..92cd038297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ## [Unreleased] ### Changed -- [android]Move DOM and render to seperate Action Object \ No newline at end of file +- [android]Move DOM and render to seperate Action Object +- [android]Move Animation(animation module and css style) to seperate Action Object +- [android]Reserve `transformOrigin`'s last time state. \ No newline at end of file diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/DOMActionContextImpl.java b/android/sdk/src/main/java/com/taobao/weex/dom/DOMActionContextImpl.java index 8a1751c85e..728c883b19 100755 --- a/android/sdk/src/main/java/com/taobao/weex/dom/DOMActionContextImpl.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/DOMActionContextImpl.java @@ -204,15 +204,13 @@ */ package com.taobao.weex.dom; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Pair; -import com.alibaba.fastjson.JSONObject; import com.taobao.weex.WXEnvironment; import com.taobao.weex.WXSDKInstance; import com.taobao.weex.WXSDKManager; +import com.taobao.weex.dom.action.Actions; import com.taobao.weex.dom.flex.CSSLayoutContext; import com.taobao.weex.ui.IWXRenderTask; import com.taobao.weex.ui.WXRenderManager; @@ -460,21 +458,11 @@ public String toString() { } private void parseAnimation() { - for(final Pair> pair:animations) { + for (final Pair> pair : animations) { if (!TextUtils.isEmpty(pair.first)) { final WXAnimationBean animationBean = createAnimationBean(pair.first, pair.second); if (animationBean != null) { - mNormalTasks.add(new IWXRenderTask() { - @Override - public void execute() { - mWXRenderManager.startAnimation(mInstanceId, pair.first, animationBean, null); - } - - @Override - public String toString() { - return "startAnimationByStyle"; - } - }); + postRenderTask(Actions.getAnimationAction(pair.first, animationBean)); } } } @@ -521,32 +509,6 @@ private void updateDomObj(WXComponent component) { } } - void startAnimation(@NonNull final String ref, @NonNull String animation, - @Nullable final String callBack){ - if (mDestroy) { - return; - } - WXDomObject domObject = mRegistry.get(ref); - if (domObject == null) { - return; - } - final WXAnimationBean animationBean=createAnimationBean(ref, animation); - if(animationBean!=null) { - mNormalTasks.add(new IWXRenderTask() { - @Override - public void execute() { - mWXRenderManager.startAnimation(mInstanceId, ref, animationBean, callBack); - } - - @Override - public String toString() { - return "startAnimationByCall"; - } - }); - mDirty=true; - } - } - @Override public void addAnimationForElement(String ref, Map animMap) { animations.add(new Pair<>(ref,animMap)); @@ -594,24 +556,6 @@ public WXDomObject getDomByRef(String ref) { return mRegistry.get(ref); } - private WXAnimationBean createAnimationBean(String ref, String animation){ - try { - WXAnimationBean animationBean = - JSONObject.parseObject(animation, WXAnimationBean.class); - if (animationBean != null && animationBean.styles != null) { - WXDomObject domObject=mRegistry.get(ref); - int width=(int)domObject.getLayoutWidth(); - int height=(int)domObject.getLayoutHeight(); - animationBean.styles.init(animationBean.styles.transformOrigin, - animationBean.styles.transform,width,height,WXSDKManager.getInstance().getSDKInstance(mInstanceId).getInstanceViewPortWidth()); - } - return animationBean; - } catch (RuntimeException e) { - WXLogUtils.e("", e); - return null; - } - } - private WXAnimationBean createAnimationBean(String ref,Map style){ if (style != null) { try { diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/WXDomHandler.java b/android/sdk/src/main/java/com/taobao/weex/dom/WXDomHandler.java index 5c3a595eb3..2f1a2f1f03 100755 --- a/android/sdk/src/main/java/com/taobao/weex/dom/WXDomHandler.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/WXDomHandler.java @@ -207,9 +207,7 @@ import android.os.Handler; import android.os.Message; -import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.taobao.weex.bridge.JSCallback; import com.taobao.weex.dom.action.Actions; /** @@ -255,12 +253,6 @@ public boolean handleMessage(Message msg) { (JSONObject) task.args.get(1), task.args.size() > 2 && (boolean) task.args.get(2)),false); break; - case MsgType.WX_ANIMATION: - mWXDomManager.startAnimation(task.instanceId, - (String) task.args.get(0), - (String) task.args.get(1), - (String) task.args.get(2)); - break; case MsgType.WX_DOM_BATCH: mWXDomManager.batch(); @@ -299,6 +291,7 @@ public static class MsgType { public static final int WX_DOM_REFRESH_FINISH = 0x0a; @Deprecated public static final int WX_DOM_UPDATE_FINISH = 0x0b; + @Deprecated public static final int WX_ANIMATION=0xc; @Deprecated public static final int WX_DOM_ADD_RULE=0xd; diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/WXDomManager.java b/android/sdk/src/main/java/com/taobao/weex/dom/WXDomManager.java index 6bb5dbb911..04ddcf3e86 100755 --- a/android/sdk/src/main/java/com/taobao/weex/dom/WXDomManager.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/WXDomManager.java @@ -206,8 +206,6 @@ import android.os.Handler; import android.os.Message; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import com.taobao.weex.WXEnvironment; import com.taobao.weex.WXSDKManager; @@ -327,20 +325,6 @@ private void throwIfNotDomThread(){ } } - void startAnimation(@NonNull String instanceId, - @NonNull String ref, - @NonNull String animation, - @Nullable String callBack){ - if (!isDomThread()) { - throw new WXRuntimeException("RefreshFinish operation must be done in dom thread"); - } - DOMActionContextImpl statement = mDomRegistries.get(instanceId); - if (statement == null) { - return; - } - statement.startAnimation(ref,animation,callBack); - } - public void executeAction(String instanceId, DOMAction action, boolean createContext) { DOMActionContext context = mDomRegistries.get(instanceId); if(context == null){ @@ -357,12 +341,20 @@ public void executeAction(String instanceId, DOMAction action, boolean createCon } - /** * @param action * @param createContext only true when create body */ public void postAction(String instanceId,DOMAction action, boolean createContext){ + postActionDelay(instanceId, action, createContext, 0); + } + + /** + * @param action + * @param createContext only true when create body + */ + public void postActionDelay(String instanceId,DOMAction action, + boolean createContext, long delay){ if(action == null){ return; } @@ -374,6 +366,6 @@ public void postAction(String instanceId,DOMAction action, boolean createContext task.args.add(action); task.args.add(createContext); msg.obj = task; - sendMessage(msg); + sendMessageDelayed(msg, delay); } } diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/action/Actions.java b/android/sdk/src/main/java/com/taobao/weex/dom/action/Actions.java index c6cc7e5954..e70dd480dc 100644 --- a/android/sdk/src/main/java/com/taobao/weex/dom/action/Actions.java +++ b/android/sdk/src/main/java/com/taobao/weex/dom/action/Actions.java @@ -204,10 +204,30 @@ */ package com.taobao.weex.dom.action; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.taobao.weex.dom.DOMAction; -import static com.taobao.weex.dom.WXDomModule.*; +import com.taobao.weex.dom.RenderAction; +import com.taobao.weex.ui.animation.WXAnimationBean; + +import static com.taobao.weex.dom.WXDomModule.ADD_ELEMENT; +import static com.taobao.weex.dom.WXDomModule.ADD_EVENT; +import static com.taobao.weex.dom.WXDomModule.ADD_RULE; +import static com.taobao.weex.dom.WXDomModule.CREATE_BODY; +import static com.taobao.weex.dom.WXDomModule.CREATE_FINISH; +import static com.taobao.weex.dom.WXDomModule.GET_COMPONENT_RECT; +import static com.taobao.weex.dom.WXDomModule.INVOKE_METHOD; +import static com.taobao.weex.dom.WXDomModule.MOVE_ELEMENT; +import static com.taobao.weex.dom.WXDomModule.REFRESH_FINISH; +import static com.taobao.weex.dom.WXDomModule.REMOVE_ELEMENT; +import static com.taobao.weex.dom.WXDomModule.REMOVE_EVENT; +import static com.taobao.weex.dom.WXDomModule.SCROLL_TO_ELEMENT; +import static com.taobao.weex.dom.WXDomModule.UPDATE_ATTRS; +import static com.taobao.weex.dom.WXDomModule.UPDATE_FINISH; +import static com.taobao.weex.dom.WXDomModule.UPDATE_STYLE; /** * Created by sospartan on 01/03/2017. @@ -311,4 +331,20 @@ public static DOMAction getUpdateStyle(String ref, JSONObject data, boolean byPe public static DOMAction getAddEvent(String ref, String type) { return new AddEventAction(ref,type); } + + public static DOMAction getAnimationAction(@NonNull final String ref, @NonNull String animation, + @Nullable final String callBack){ + return new AnimationAction(ref, animation, callBack); + } + + public static RenderAction getAnimationAction(@NonNull String ref, + @NonNull final WXAnimationBean animationBean){ + return new AnimationAction(ref, animationBean); + } + + public static RenderAction getAnimationAction(@NonNull String ref, + @NonNull final WXAnimationBean animationBean, + @Nullable String callback){ + return new AnimationAction(ref, animationBean, callback); + } } diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/action/AnimationAction.java b/android/sdk/src/main/java/com/taobao/weex/dom/action/AnimationAction.java new file mode 100644 index 0000000000..976801638e --- /dev/null +++ b/android/sdk/src/main/java/com/taobao/weex/dom/action/AnimationAction.java @@ -0,0 +1,463 @@ +/* + * + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright 2016 Alibaba Group + * + * 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.taobao.weex.dom.action; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.animation.PathInterpolatorCompat; +import android.text.TextUtils; +import android.util.Pair; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import com.alibaba.fastjson.JSONObject; +import com.taobao.weex.WXSDKInstance; +import com.taobao.weex.WXSDKManager; +import com.taobao.weex.dom.DOMAction; +import com.taobao.weex.dom.DOMActionContext; +import com.taobao.weex.dom.RenderAction; +import com.taobao.weex.dom.RenderActionContext; +import com.taobao.weex.dom.WXDomObject; +import com.taobao.weex.ui.animation.BackgroundColorProperty; +import com.taobao.weex.ui.animation.DimensionUpdateListener; +import com.taobao.weex.ui.animation.WXAnimationBean; +import com.taobao.weex.ui.animation.WXAnimationModule; +import com.taobao.weex.ui.component.WXComponent; +import com.taobao.weex.ui.view.border.BorderDrawable; +import com.taobao.weex.utils.SingleFunctionParser; +import com.taobao.weex.utils.WXLogUtils; +import com.taobao.weex.utils.WXResourceUtils; +import com.taobao.weex.utils.WXUtils; +import com.taobao.weex.utils.WXViewUtils; + +import java.util.HashMap; +import java.util.List; + + +class AnimationAction implements DOMAction, RenderAction { + + private final static String TAG = "AnimationAction"; + @NonNull + private final String ref; + + @Nullable + private + final String animation; + + @Nullable + private + final String callback; + + @Nullable + private + WXAnimationBean mAnimationBean; + + AnimationAction(@NonNull final String ref, @Nullable String animation, + @Nullable final String callBack) { + this.ref = ref; + this.animation = animation; + this.callback = callBack; + } + + AnimationAction(@NonNull String ref, @NonNull WXAnimationBean animationBean) { + this(ref, animationBean, null); + } + + AnimationAction(@NonNull String ref, @NonNull WXAnimationBean animationBean, + @Nullable final String callBack) { + this.ref = ref; + this.mAnimationBean = animationBean; + this.callback = callBack; + this.animation = null; + } + + @Override + public void executeDom(DOMActionContext context) { + try { + WXDomObject domObject; + if (!context.isDestory() && + !TextUtils.isEmpty(animation) && + (domObject = context.getDomByRef(ref)) != null) { + WXAnimationBean animationBean = JSONObject.parseObject(animation, WXAnimationBean.class); + if (animationBean != null && animationBean.styles != null) { + int width = (int) domObject.getLayoutWidth(); + int height = (int) domObject.getLayoutHeight(); + animationBean.styles.init(animationBean.styles.transformOrigin, + animationBean.styles.transform, width, height, + context.getInstance().getInstanceViewPortWidth()); + mAnimationBean = animationBean; + context.postRenderTask(this); + } + } + } catch (RuntimeException e) { + WXLogUtils.e(TAG, WXLogUtils.getStackTrace(e)); + } + } + + @Override + public void executeRender(RenderActionContext context) { + WXSDKInstance instance; + if (mAnimationBean != null && (instance = context.getInstance()) != null) { + startAnimation(instance, context.getComponent(ref)); + } + } + + private void startAnimation(@NonNull WXSDKInstance instance, @Nullable WXComponent component) { + if (component != null) { + if (component.getHostView() == null) { + WXAnimationModule.AnimationHolder holder = new WXAnimationModule.AnimationHolder(mAnimationBean, callback); + component.postAnimation(holder); + } else { + try { + Animator animator = createAnimator(component.getHostView(), instance + .getInstanceViewPortWidth()); + if (animator != null) { + Animator.AnimatorListener animatorCallback = createAnimatorListener(instance, callback); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + component.getHostView().setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + Interpolator interpolator = createTimeInterpolator(); + if (animatorCallback != null) { + animator.addListener(animatorCallback); + } + if (interpolator != null) { + animator.setInterpolator(interpolator); + } + animator.setDuration(mAnimationBean.duration); + animator.start(); + } + } catch (RuntimeException e) { + WXLogUtils.e(TAG, WXLogUtils.getStackTrace(e)); + } + } + } + } + + private + @Nullable + ObjectAnimator createAnimator(final View target, final int viewPortWidth) { + if (target == null) { + return null; + } + WXAnimationBean.Style style = mAnimationBean.styles; + if (style != null) { + ObjectAnimator animator; + List holders = style.getHolders(); + if (!TextUtils.isEmpty(style.backgroundColor)) { + BorderDrawable borderDrawable; + if ((borderDrawable = WXViewUtils.getBorderDrawable(target)) != null) { + holders.add(PropertyValuesHolder.ofObject( + new BackgroundColorProperty(), new ArgbEvaluator(), + borderDrawable.getColor(), + WXResourceUtils.getColor(style.backgroundColor))); + } else if (target.getBackground() instanceof ColorDrawable) { + holders.add(PropertyValuesHolder.ofObject( + new BackgroundColorProperty(), new ArgbEvaluator(), + ((ColorDrawable) target.getBackground()).getColor(), + WXResourceUtils.getColor(style.backgroundColor))); + } + } + if (style.getPivot() != null) { + Pair pair = style.getPivot(); + target.setPivotX(pair.first); + target.setPivotY(pair.second); + } + animator = ObjectAnimator.ofPropertyValuesHolder( + target, holders.toArray(new PropertyValuesHolder[holders.size()])); + animator.setStartDelay(mAnimationBean.delay); + if (target.getLayoutParams() != null && + (!TextUtils.isEmpty(style.width) || !TextUtils.isEmpty(style.height))) { + DimensionUpdateListener listener = new DimensionUpdateListener(target); + ViewGroup.LayoutParams layoutParams = target.getLayoutParams(); + if (!TextUtils.isEmpty(style.width)) { + listener.setWidth(layoutParams.width, + (int) WXViewUtils.getRealPxByWidth(WXUtils.getFloat(style.width), viewPortWidth)); + } + if (!TextUtils.isEmpty(style.height)) { + listener.setHeight(layoutParams.height, + (int) WXViewUtils.getRealPxByWidth(WXUtils.getFloat(style.height), viewPortWidth)); + } + animator.addUpdateListener(listener); + } + return animator; + } else { + return null; + } + } + + private + @Nullable + Animator.AnimatorListener createAnimatorListener(final WXSDKInstance instance, @Nullable final String callBack) { + if (!TextUtils.isEmpty(callBack)) { + return new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (instance == null) { + WXLogUtils.e("RenderActionContextImpl-onAnimationEnd WXSDKInstance == null NPE"); + } else { + WXSDKManager.getInstance().callback(instance.getInstanceId(), + callBack, + new HashMap()); + } + } + }; + } else { + return null; + } + } + + private + @Nullable + Interpolator createTimeInterpolator() { + String interpolator = mAnimationBean.timingFunction; + if (!TextUtils.isEmpty(interpolator)) { + switch (interpolator) { + case WXAnimationBean.EASE_IN: + return new AccelerateInterpolator(); + case WXAnimationBean.EASE_OUT: + return new DecelerateInterpolator(); + case WXAnimationBean.EASE_IN_OUT: + return new AccelerateDecelerateInterpolator(); + case WXAnimationBean.LINEAR: + return new LinearInterpolator(); + default: + //Parse cubic-bezier + try { + SingleFunctionParser parser = new SingleFunctionParser<>( + mAnimationBean.timingFunction, + new SingleFunctionParser.FlatMapper() { + @Override + public Float map(String raw) { + return Float.parseFloat(raw); + } + }); + List params = parser.parse(WXAnimationBean.CUBIC_BEZIER); + if (params != null && params.size() == WXAnimationBean.NUM_CUBIC_PARAM) { + return PathInterpolatorCompat.create( + params.get(0), params.get(1), params.get(2), params.get(3)); + } else { + return null; + } + } catch (RuntimeException e) { + return null; + } + } + } + return null; + } +} diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/RenderActionContextImpl.java b/android/sdk/src/main/java/com/taobao/weex/ui/RenderActionContextImpl.java index 1093952499..d694219c2f 100755 --- a/android/sdk/src/main/java/com/taobao/weex/ui/RenderActionContextImpl.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/RenderActionContextImpl.java @@ -289,10 +289,6 @@ public WXComponent getComponent(String ref) { return mRegistry.get(ref); } - void startAnimation(@NonNull String ref, @NonNull WXAnimationBean animationBean, @Nullable String callBack) { - WXAnimationModule.startAnimation(mWXSDKInstance, mRegistry.get(ref), animationBean, callBack); - } - public void registerComponent(String ref, WXComponent comp) { mRegistry.put(ref,comp); } diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/WXRenderManager.java b/android/sdk/src/main/java/com/taobao/weex/ui/WXRenderManager.java index d8a81d6cc1..3b229fd784 100755 --- a/android/sdk/src/main/java/com/taobao/weex/ui/WXRenderManager.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/WXRenderManager.java @@ -204,7 +204,6 @@ */ package com.taobao.weex.ui; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; @@ -214,7 +213,6 @@ import com.taobao.weex.dom.RenderAction; import com.taobao.weex.dom.RenderActionContext; import com.taobao.weex.dom.WXDomObject; -import com.taobao.weex.ui.animation.WXAnimationBean; import com.taobao.weex.ui.component.WXComponent; import com.taobao.weex.utils.WXUtils; @@ -325,16 +323,6 @@ public void setExtra(String instanceId, String ref, Object extra) { statement.setExtra(ref, extra); } - public void startAnimation(String instanceId, @NonNull String ref, - @NonNull WXAnimationBean animationBean, @Nullable String - callBack) { - RenderActionContextImpl statement = mRegistries.get(instanceId); - if (statement == null) { - return; - } - statement.startAnimation(ref, animationBean, callBack); - } - public List getAllInstances() { ArrayList instances = null; if (mRegistries != null && !mRegistries.isEmpty()) { diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/animation/DimensionUpdateListener.java b/android/sdk/src/main/java/com/taobao/weex/ui/animation/DimensionUpdateListener.java index 43117f5648..74d3070c91 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/animation/DimensionUpdateListener.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/animation/DimensionUpdateListener.java @@ -220,16 +220,16 @@ public class DimensionUpdateListener implements ValueAnimator.AnimatorUpdateList private Pair height; private IntEvaluator intEvaluator; - DimensionUpdateListener(@NonNull View view) { + public DimensionUpdateListener(@NonNull View view) { this.view = view; intEvaluator = new IntEvaluator(); } - void setWidth(int from, int to) { + public void setWidth(int from, int to) { width = new Pair<>(from, to); } - void setHeight(int from, int to) { + public void setHeight(int from, int to) { height = new Pair<>(from, to); } diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationModule.java b/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationModule.java index a06ae414ea..26fc9def52 100755 --- a/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationModule.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationModule.java @@ -204,220 +204,47 @@ */ package com.taobao.weex.ui.animation; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ArgbEvaluator; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.graphics.drawable.ColorDrawable; -import android.os.Build; -import android.os.Message; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.view.animation.PathInterpolatorCompat; import android.text.TextUtils; -import android.util.Pair; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; import com.taobao.weex.WXSDKInstance; import com.taobao.weex.WXSDKManager; import com.taobao.weex.annotation.JSMethod; import com.taobao.weex.common.WXModule; +import com.taobao.weex.dom.DOMAction; +import com.taobao.weex.dom.RenderAction; import com.taobao.weex.dom.WXDomHandler; -import com.taobao.weex.dom.WXDomTask; +import com.taobao.weex.dom.action.Actions; import com.taobao.weex.ui.component.WXComponent; -import com.taobao.weex.ui.view.border.BorderDrawable; -import com.taobao.weex.utils.SingleFunctionParser; -import com.taobao.weex.utils.WXLogUtils; -import com.taobao.weex.utils.WXResourceUtils; -import com.taobao.weex.utils.WXUtils; -import com.taobao.weex.utils.WXViewUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import static com.taobao.weex.dom.action.Actions.getAnimationAction; public class WXAnimationModule extends WXModule { @JSMethod public void transition(@Nullable String ref, @Nullable String animation, @Nullable String callBack) { - if(!TextUtils.isEmpty(ref)&&!TextUtils.isEmpty(animation) && mWXSDKInstance!=null) { - Message msg = Message.obtain(); - WXDomTask task = new WXDomTask(); - task.instanceId = mWXSDKInstance.getInstanceId(); - task.args = new ArrayList<>(); - task.args.add(ref); - task.args.add(animation); - task.args.add(callBack); - msg.what = WXDomHandler.MsgType.WX_ANIMATION; - msg.obj = task; + if (!TextUtils.isEmpty(ref) && !TextUtils.isEmpty(animation) && mWXSDKInstance != null) { + DOMAction animationActions = getAnimationAction(ref, animation, callBack); //Due to animation module rely on the result of the css-layout and the batch mechanism of //css-layout, the animation.transition must be delayed the batch time. - WXSDKManager.getInstance().getWXDomManager().sendMessageDelayed(msg, WXDomHandler.DELAY_TIME); + WXSDKManager.getInstance().getWXDomManager().postActionDelay(mWXSDKInstance.getInstanceId(), + animationActions, + false, WXDomHandler.DELAY_TIME); } } - public static void startAnimation(WXSDKInstance instance, WXComponent component, - @NonNull WXAnimationBean animationBean, @Nullable String callback) { - if(component == null){ - return; - } - if (component.getHostView() == null) { - AnimationHolder holder = new AnimationHolder(animationBean, callback); - component.postAnimation(holder); - return; - } - try { - Animator animator = createAnimator(animationBean, component.getHostView(),instance.getInstanceViewPortWidth()); - if (animator != null) { - Animator.AnimatorListener animatorCallback = createAnimatorListener(instance, callback); - if(Build.VERSION.SDK_INT holders =style.getHolders(); - if (!TextUtils.isEmpty(style.backgroundColor)) { - BorderDrawable borderDrawable; - if ((borderDrawable=WXViewUtils.getBorderDrawable(target))!=null) { - holders.add(PropertyValuesHolder.ofObject( - new BackgroundColorProperty(), new ArgbEvaluator(), - borderDrawable.getColor(), - WXResourceUtils.getColor(style.backgroundColor))); - } else if (target.getBackground() instanceof ColorDrawable) { - holders.add(PropertyValuesHolder.ofObject( - new BackgroundColorProperty(), new ArgbEvaluator(), - ((ColorDrawable) target.getBackground()).getColor(), - WXResourceUtils.getColor(style.backgroundColor))); - } - } - if (style.getPivot() != null) { - Pair pair = style.getPivot(); - target.setPivotX(pair.first); - target.setPivotY(pair.second); - } - animator = ObjectAnimator.ofPropertyValuesHolder( - target, holders.toArray(new PropertyValuesHolder[holders.size()])); - animator.setStartDelay(animation.delay); - if (target.getLayoutParams() != null && - (!TextUtils.isEmpty(style.width) || !TextUtils.isEmpty(style.height))) { - DimensionUpdateListener listener = new DimensionUpdateListener(target); - ViewGroup.LayoutParams layoutParams = target.getLayoutParams(); - if (!TextUtils.isEmpty(style.width)) { - listener.setWidth(layoutParams.width, - (int) WXViewUtils.getRealPxByWidth(WXUtils.getFloat(style.width),viewPortWidth)); - } - if (!TextUtils.isEmpty(style.height)) { - listener.setHeight(layoutParams.height, - (int) WXViewUtils.getRealPxByWidth(WXUtils.getFloat(style.height),viewPortWidth)); - } - animator.addUpdateListener(listener); - } - return animator; - } else { - return null; - } - } - - public static - @Nullable - Animator.AnimatorListener createAnimatorListener(final WXSDKInstance instance, @Nullable final String callBack) { - if (!TextUtils.isEmpty(callBack)) { - return new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (instance == null) { - WXLogUtils.e("RenderActionContextImpl-onAnimationEnd WXSDKInstance == null NPE"); - } else { - WXSDKManager.getInstance().callback(instance.getInstanceId(), - callBack, - new HashMap()); - } - } - }; - } else { - return null; - } - } - - private static @Nullable - Interpolator createTimeInterpolator(@NonNull WXAnimationBean animation) { - String interpolator = animation.timingFunction; - if (!TextUtils.isEmpty(interpolator)) { - switch (interpolator) { - case WXAnimationBean.EASE_IN: - return new AccelerateInterpolator(); - case WXAnimationBean.EASE_OUT: - return new DecelerateInterpolator(); - case WXAnimationBean.EASE_IN_OUT: - return new AccelerateDecelerateInterpolator(); - case WXAnimationBean.LINEAR: - return new LinearInterpolator(); - default: - //Parse cubic-bezier - try { - SingleFunctionParser parser = new SingleFunctionParser<>( - animation.timingFunction, - new SingleFunctionParser.FlatMapper() { - @Override - public Float map(String raw) { - return Float.parseFloat(raw); - } - }); - List params = parser.parse(WXAnimationBean.CUBIC_BEZIER); - if (params != null && params.size() == WXAnimationBean.NUM_CUBIC_PARAM) { - return PathInterpolatorCompat.create( - params.get(0), params.get(1), params.get(2), params.get(3)); - } - else { - return null; - } - }catch (RuntimeException e){ - return null; - } - } - } - return null; - } - //add by moxun on 12/26/2016 public static class AnimationHolder { + private WXAnimationBean wxAnimationBean; private String callback; public void execute(WXSDKInstance mInstance, WXComponent component) { - WXAnimationModule.startAnimation(mInstance, component, wxAnimationBean, callback); + RenderAction action = Actions.getAnimationAction(component.getRef(), wxAnimationBean, callback); + WXSDKManager.getInstance().getWXRenderManager().runOnThread(mInstance.getInstanceId(), action); } - private AnimationHolder(WXAnimationBean wxAnimationBean, String callback) { + public AnimationHolder(WXAnimationBean wxAnimationBean, String callback) { this.wxAnimationBean = wxAnimationBean; this.callback = callback; } diff --git a/android/sdk/src/test/java/com/taobao/weex/dom/WXDomStatementTest.java b/android/sdk/src/test/java/com/taobao/weex/dom/WXDomStatementTest.java index c0f172452b..9a10870730 100644 --- a/android/sdk/src/test/java/com/taobao/weex/dom/WXDomStatementTest.java +++ b/android/sdk/src/test/java/com/taobao/weex/dom/WXDomStatementTest.java @@ -476,15 +476,4 @@ public void testUpdateFinish() throws Exception { updateFinish(); stmt.batch(); } - - @Test - public void testStartAnimation() throws Exception { - createBody(); - JSONObject obj; - obj = new JSONObject(); - obj.put("type","div"); - obj.put("ref","100"); - addDom(obj,WXDomObject.ROOT,0); - stmt.startAnimation("100","",null); - } } \ No newline at end of file diff --git a/android/sdk/src/test/java/com/taobao/weex/ui/animation/WXAnimationModuleTest.java b/android/sdk/src/test/java/com/taobao/weex/ui/animation/WXAnimationModuleTest.java index ffeb3a00d4..0f6c298712 100644 --- a/android/sdk/src/test/java/com/taobao/weex/ui/animation/WXAnimationModuleTest.java +++ b/android/sdk/src/test/java/com/taobao/weex/ui/animation/WXAnimationModuleTest.java @@ -206,8 +206,7 @@ import com.taobao.weappplus_sdk.BuildConfig; import com.taobao.weex.WXSDKInstanceTest; -import com.taobao.weex.dom.TestDomObject; -import com.taobao.weex.ui.component.TestComponent; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -241,21 +240,6 @@ public void testTransition() throws Exception { module.transition("test","test",""); } - @Test - public void testStartAnimation() throws Exception { - module.startAnimation(module.mWXSDKInstance,null,null,null); - - TestComponent comp = new TestComponent(module.mWXSDKInstance,new TestDomObject(),null); - module.startAnimation(module.mWXSDKInstance,comp,null,null); - - WXAnimationBean animation = new WXAnimationBean(); - module.startAnimation(module.mWXSDKInstance,comp,animation,null); - - animation.styles = new WXAnimationBean.Style(); - module.startAnimation(module.mWXSDKInstance,comp,animation,null); - - } - @Test public void testCreateAnimatorListener() throws Exception { From 4d54b24271a57e240c8d54a17776863ec0a469fb Mon Sep 17 00:00:00 2001 From: YorkShen Date: Fri, 17 Mar 2017 11:01:28 +0800 Subject: [PATCH 2/2] * [android] Make `transformOrigin` reserve its last time state. Before this commit, transformOrigin is stateless during a sequence of animation, e.g. tranformOrigin would reset to `50%, 50%` every time if it was not given. This commit respect `transformOrigin` last time state unless it is specified this time. Developers who depends on the wrong implementation may run into a problem. Potential break changes for Front-end developer: Those who depends on transformOrigin reset to `center center` after each animation will run into problem. --- .../java/com/taobao/weex/ui/animation/WXAnimationBean.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationBean.java b/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationBean.java index 7d89db1238..01f1991c44 100755 --- a/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationBean.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/animation/WXAnimationBean.java @@ -411,8 +411,7 @@ private static Pair parsePivot(@Nullable String transformOrigin, } } } - return parsePivot(Arrays.asList(WXAnimationBean.Style.CENTER, - WXAnimationBean.Style.CENTER), width, height,viewportW); + return null; } private static Pair parsePivot(@NonNull List list, int width, int height,int viewportW) { @@ -469,7 +468,7 @@ private static float parsePercent(String percent, int unit, int precision) { return defaultMap; } - public Pair getPivot() { + public @Nullable Pair getPivot() { return pivot; }