| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.ui.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.common.Constants; |
| import com.taobao.weex.ui.animation.BackgroundColorProperty; |
| import com.taobao.weex.ui.animation.HeightProperty; |
| import com.taobao.weex.ui.animation.WXAnimationBean; |
| import com.taobao.weex.ui.animation.WXAnimationModule; |
| import com.taobao.weex.ui.animation.WidthProperty; |
| import com.taobao.weex.ui.component.WXComponent; |
| import com.taobao.weex.ui.component.list.template.TemplateDom; |
| 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; |
| |
| public class GraphicActionAnimation extends BasicGraphicAction { |
| |
| private final static String TAG = "GraphicActionAnimation"; |
| |
| private final boolean styleNeedInit; |
| |
| @Nullable |
| private |
| final String callback; |
| |
| @Nullable |
| private |
| WXAnimationBean mAnimationBean; |
| |
| public GraphicActionAnimation(@NonNull WXSDKInstance instance, @NonNull String ref, @NonNull WXAnimationBean animationBean) { |
| super(instance, ref); |
| this.styleNeedInit = false; |
| this.callback = null; |
| this.mAnimationBean = animationBean; |
| } |
| |
| public GraphicActionAnimation(@NonNull WXSDKInstance instance, @NonNull String ref, @Nullable String animation, |
| @Nullable final String callBack) { |
| super(instance, ref); |
| this.styleNeedInit = true; |
| this.callback = callBack; |
| if (!TextUtils.isEmpty(animation)) { |
| this.mAnimationBean = JSONObject.parseObject(animation, WXAnimationBean.class); |
| } |
| } |
| public GraphicActionAnimation(@NonNull WXSDKInstance instance, @NonNull String ref, @NonNull WXAnimationBean animationBean, |
| @Nullable final String callBack) { |
| super(instance, ref); |
| this.styleNeedInit = false; |
| this.mAnimationBean = animationBean; |
| this.callback = callBack; |
| } |
| |
| @Override |
| public void executeAction() { |
| if (null == mAnimationBean) { |
| return; |
| } |
| |
| WXComponent component = WXSDKManager.getInstance().getWXRenderManager().getWXComponent(getPageId(), getRef()); |
| if (component == null) { |
| if(!TemplateDom.isVirtualDomRef(getRef())){ |
| return; |
| } |
| component = TemplateDom.findVirtualComponentByVRef(getPageId(), getRef()); |
| if(component == null){ |
| return; |
| } |
| } |
| |
| WXSDKInstance instance = WXSDKManager.getInstance().getWXRenderManager().getWXSDKInstance(getPageId()); |
| if (instance == null) { |
| return; |
| } |
| |
| if (null != mAnimationBean.styles) { |
| if(styleNeedInit) { |
| // Synchronize transformOrigin between component styles and animation style before |
| // animation start. |
| String transformOrigin = (String) component.getStyles().get(Constants.Name.TRANSFORM_ORIGIN); |
| if (TextUtils.isEmpty(mAnimationBean.styles.transformOrigin)) { |
| mAnimationBean.styles.transformOrigin = transformOrigin; |
| } |
| mAnimationBean.styles.init(mAnimationBean.styles.transformOrigin, |
| mAnimationBean.styles.transform, (int) component.getLayoutWidth(), |
| (int) component.getLayoutHeight(), |
| instance.getInstanceViewPortWidth(), instance); |
| } |
| startAnimation(instance, component); |
| } |
| } |
| |
| |
| |
| private void startAnimation(@NonNull WXSDKInstance instance, @Nullable WXComponent component) { |
| if (component != null) { |
| if (mAnimationBean != null) { |
| component.setNeedLayoutOnAnimation(mAnimationBean.needLayout); |
| } |
| 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 |
| .isLayerTypeEnabled() ) { |
| component.getHostView().setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| } |
| Interpolator interpolator = createTimeInterpolator(); |
| if (animatorCallback != null) { |
| animator.addListener(animatorCallback); |
| } |
| if (interpolator != null) { |
| animator.setInterpolator(interpolator); |
| } |
| component.getHostView().setCameraDistance(mAnimationBean.styles.getCameraDistance()); |
| 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<PropertyValuesHolder> 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 (target.getLayoutParams() != null && |
| (!TextUtils.isEmpty(style.width) || !TextUtils.isEmpty(style.height))) { |
| ViewGroup.LayoutParams layoutParams = target.getLayoutParams(); |
| if (!TextUtils.isEmpty(style.width)) { |
| holders.add(PropertyValuesHolder.ofInt(new WidthProperty(), layoutParams.width, |
| (int) WXViewUtils.getRealPxByWidth(WXUtils.getFloat(style.width), viewPortWidth))); |
| } |
| if (!TextUtils.isEmpty(style.height)) { |
| holders.add(PropertyValuesHolder.ofInt(new HeightProperty(), layoutParams.height, |
| (int) WXViewUtils.getRealPxByWidth(WXUtils.getFloat(style.height), viewPortWidth))); |
| } |
| } |
| |
| if (style.getPivot() != null) { |
| Pair<Float, Float> 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); |
| 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 || instance.isDestroy()) { |
| WXLogUtils.e("RenderContextImpl-onAnimationEnd WXSDKInstance == null NPE or instance is destroyed"); |
| } else { |
| WXSDKManager.getInstance().callback(instance.getInstanceId(), |
| callBack, |
| new HashMap<String, Object>()); |
| } |
| } |
| }; |
| } 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<Float> parser = new SingleFunctionParser<>( |
| mAnimationBean.timingFunction, |
| new SingleFunctionParser.FlatMapper<Float>() { |
| @Override |
| public Float map(String raw) { |
| return Float.parseFloat(raw); |
| } |
| }); |
| List<Float> 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; |
| } |
| } |