com.taobao.weex.dom.action.AnimationAction.java Source code

Java tutorial

Introduction

Here is the source code for com.taobao.weex.dom.action.AnimationAction.java

Source

/*
 * 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.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.isLayerTypeEnabled()) {
                            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<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 (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);
            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<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;
    }
}