Java tutorial
/* * 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; } }