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.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.support.v4.util.ArrayMap; import android.support.v4.view.animation.PathInterpolatorCompat; import android.text.TextUtils; import android.util.Property; import android.view.View; import android.view.animation.Interpolator; import com.taobao.weex.WXEnvironment; import com.taobao.weex.WXSDKManager; import com.taobao.weex.common.Constants; import com.taobao.weex.dom.DOMActionContext; import com.taobao.weex.dom.WXDomHandler; import com.taobao.weex.dom.WXDomObject; import com.taobao.weex.dom.flex.Spacing; import com.taobao.weex.ui.animation.BackgroundColorProperty; import com.taobao.weex.ui.animation.TransformParser; import com.taobao.weex.ui.component.WXComponent; 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.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import static com.taobao.weex.common.Constants.TimeFunction.CUBIC_BEZIER; import static com.taobao.weex.common.Constants.TimeFunction.EASE; import static com.taobao.weex.common.Constants.TimeFunction.EASE_IN; import static com.taobao.weex.common.Constants.TimeFunction.EASE_IN_OUT; import static com.taobao.weex.common.Constants.TimeFunction.EASE_OUT; import static com.taobao.weex.common.Constants.TimeFunction.LINEAR; /** * transition on dom thread * transition-property: height; * transition-duration: .3s; * transition-delay: .05s; * transition-timing-function: ease-in-out; * * Created by furture on 2017/10/18. */ public class WXTransition { public static final String TRANSITION_PROPERTY = "transitionProperty"; public static final String TRANSITION_DURATION = "transitionDuration"; public static final String TRANSITION_DELAY = "transitionDelay"; public static final String TRANSITION_TIMING_FUNCTION = "transitionTimingFunction"; public static final Pattern PROPERTY_SPLIT_PATTERN = Pattern.compile("\\||,"); /** * layout animation property * */ private static final Set<String> LAYOUT_PROPERTIES = new HashSet<>(); static { LAYOUT_PROPERTIES.add(Constants.Name.WIDTH); LAYOUT_PROPERTIES.add(Constants.Name.HEIGHT); LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_TOP); LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_BOTTOM); LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_LEFT); LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_RIGHT); LAYOUT_PROPERTIES.add(Constants.Name.LEFT); LAYOUT_PROPERTIES.add(Constants.Name.RIGHT); LAYOUT_PROPERTIES.add(Constants.Name.TOP); LAYOUT_PROPERTIES.add(Constants.Name.BOTTOM); LAYOUT_PROPERTIES.add(Constants.Name.PADDING_LEFT); LAYOUT_PROPERTIES.add(Constants.Name.PADDING_RIGHT); LAYOUT_PROPERTIES.add(Constants.Name.PADDING_TOP); LAYOUT_PROPERTIES.add(Constants.Name.PADDING_BOTTOM); } /** * transform animation property, use android system animaton ability * */ private static final Set<String> TRANSFORM_PROPERTIES = new HashSet<>(); static { TRANSFORM_PROPERTIES.add(Constants.Name.OPACITY); TRANSFORM_PROPERTIES.add(Constants.Name.BACKGROUND_COLOR); TRANSFORM_PROPERTIES.add(Constants.Name.TRANSFORM); } private List<String> properties; private Interpolator interpolator; private long duration; private long delay; private WXDomObject domObject; private Handler handler; private ValueAnimator layoutValueAnimator; private Map<String, Object> layoutPendingUpdates; private ObjectAnimator transformAnimator; private Map<String, Object> transformPendingUpdates; private Runnable transitionEndEvent; private Map<String, Object> targetStyles; private Runnable animationRunnable; private Runnable transformAnimationRunnable; private volatile AtomicInteger lockToken = new AtomicInteger(0); public WXTransition() { this.properties = new ArrayList<>(4); this.handler = new Handler(); this.layoutPendingUpdates = new ArrayMap<>(); this.transformPendingUpdates = new ArrayMap<>(); this.targetStyles = new ArrayMap<>(); } /** * create transition from map styles if style contains transitionProperty * */ public static WXTransition fromMap(Map<String, Object> style, WXDomObject domObject) { if (style.get(TRANSITION_PROPERTY) == null) { return null; } String propertyString = WXUtils.getString(style.get(TRANSITION_PROPERTY), null); if (propertyString == null) { return null; } WXTransition transition = new WXTransition(); updateTransitionProperties(transition, propertyString); if (transition.properties.isEmpty()) { return null; } transition.duration = parseTimeMillis(style, TRANSITION_DURATION, 0); transition.delay = parseTimeMillis(style, TRANSITION_DELAY, 0); transition.interpolator = createTimeInterpolator( WXUtils.getString(style.get(TRANSITION_TIMING_FUNCTION), null)); transition.domObject = domObject; return transition; } /** * check updates has transition property * */ public boolean hasTransitionProperty(Map<String, Object> styles) { for (String property : properties) { if (styles.containsKey(property)) { return true; } } return false; } public void updateTranstionParams(Map<String, Object> updates) { if (updates.containsKey(TRANSITION_DELAY)) { domObject.getStyles().put(TRANSITION_DELAY, updates.remove(TRANSITION_DELAY)); this.delay = parseTimeMillis(domObject.getStyles(), TRANSITION_DELAY, 0); } if (updates.containsKey(TRANSITION_TIMING_FUNCTION) && updates.get(TRANSITION_TIMING_FUNCTION) != null) { domObject.getStyles().put(TRANSITION_TIMING_FUNCTION, updates.remove(TRANSITION_TIMING_FUNCTION)); this.interpolator = createTimeInterpolator( domObject.getStyles().get(TRANSITION_TIMING_FUNCTION).toString()); } if (updates.containsKey(TRANSITION_DURATION)) { domObject.getStyles().put(TRANSITION_DURATION, updates.remove(TRANSITION_DURATION)); this.duration = parseTimeMillis(domObject.getStyles(), TRANSITION_DURATION, 0); } if (updates.containsKey(TRANSITION_PROPERTY)) { domObject.getStyles().put(TRANSITION_PROPERTY, updates.remove(TRANSITION_PROPERTY)); updateTransitionProperties(this, WXUtils.getString(domObject.getStyles().get(TRANSITION_PROPERTY), null)); } } /** * start transition animation, updates maybe split two different updates, * because javascript will send multi update on same transition, we assume that updates in 8ms is one transition * */ public void startTransition(Map<String, Object> updates) { synchronized (lockToken) { final View taregtView = getTargetView(); if (taregtView == null) { return; } final int token = lockToken.incrementAndGet(); for (String property : properties) { if (updates.containsKey(property)) { Object targetValue = updates.remove(property); if (LAYOUT_PROPERTIES.contains(property)) { layoutPendingUpdates.put(property, targetValue); } else if (TRANSFORM_PROPERTIES.contains(property)) { transformPendingUpdates.put(property, targetValue); } } } int delay = WXUtils.getNumberInt(domObject.getAttrs().get("actionDelay"), 16); if (delay > duration) { delay = (int) duration; } if (animationRunnable != null) { handler.removeCallbacks(animationRunnable); } animationRunnable = new Runnable() { @Override public void run() { if (token == lockToken.get()) { doTransitionAnimation(token); } animationRunnable = null; } }; if (delay > 0) { handler.postDelayed(animationRunnable, delay); } else { animationRunnable.run(); } } } /** * doTransitionAnimation include transform and layout animation. * 1. put pre transition updates from target style to dom style * 2. do transform animation and layout animation * */ private void doTransitionAnimation(final int token) { final View taregtView = getTargetView(); if (taregtView == null) { return; } if (targetStyles.size() > 0) { for (String property : properties) { if (!(LAYOUT_PROPERTIES.contains(property) || TRANSFORM_PROPERTIES.contains(property))) { continue; } if (layoutPendingUpdates.containsKey(property)) { continue; } if (transformPendingUpdates.containsKey(property)) { continue; } synchronized (targetStyles) { if (targetStyles.containsKey(property)) { //reset pre transition style Object targetValue = targetStyles.remove(property); domObject.getStyles().put(property, targetValue); WXComponent component = getComponent(); if (component != null && component.getDomObject() != null) { component.getDomObject().getStyles().put(property, targetValue); } } } } } if (transitionEndEvent != null) { taregtView.removeCallbacks(transitionEndEvent); } if (transitionEndEvent == null && duration > Float.MIN_NORMAL) { transitionEndEvent = new Runnable() { @Override public void run() { transitionEndEvent = null; if (duration < Float.MIN_NORMAL) { return; } WXComponent component = getComponent(); if (component != null && domObject.getEvents().contains(Constants.Event.ON_TRANSITION_END)) { component.fireEvent(Constants.Event.ON_TRANSITION_END); } } }; } if (transformAnimationRunnable != null) { taregtView.removeCallbacks(transformAnimationRunnable); } transformAnimationRunnable = new Runnable() { @Override public void run() { synchronized (lockToken) { if (token == lockToken.get()) { doPendingTransformAnimation(token); } } } }; taregtView.post(transformAnimationRunnable); doPendingLayoutAnimation(); } /** * transform, opacity, backgroundcolor which not effect layout use android system animation in main thread. * */ private void doPendingTransformAnimation(int token) { if (transformAnimator != null) { transformAnimator.cancel(); transformAnimator = null; } if (transformPendingUpdates.size() == 0) { return; } final View taregtView = getTargetView(); if (taregtView == null) { return; } List<PropertyValuesHolder> holders = new ArrayList<>(8); String transform = WXUtils.getString(transformPendingUpdates.remove(Constants.Name.TRANSFORM), null); if (!TextUtils.isEmpty(transform)) { Map<Property<View, Float>, Float> properties = TransformParser.parseTransForm(transform, (int) domObject.getLayoutWidth(), (int) domObject.getLayoutHeight(), domObject.getViewPortWidth()); PropertyValuesHolder[] transformHolders = TransformParser.toHolders(properties); for (PropertyValuesHolder holder : transformHolders) { holders.add(holder); } synchronized (targetStyles) { targetStyles.put(Constants.Name.TRANSFORM, transform); } } for (String property : properties) { if (!TRANSFORM_PROPERTIES.contains(property)) { continue; } if (!transformPendingUpdates.containsKey(property)) { continue; } Object value = transformPendingUpdates.remove(property); synchronized (targetStyles) { targetStyles.put(property, value); } switch (property) { case Constants.Name.OPACITY: { holders.add(PropertyValuesHolder.ofFloat(View.ALPHA, taregtView.getAlpha(), WXUtils.getFloat(value, 1.0f))); taregtView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); //hardware or none has bug on some platform } break; case Constants.Name.BACKGROUND_COLOR: { int fromColor = WXResourceUtils .getColor(WXUtils.getString(domObject.getStyles().getBackgroundColor(), null), 0); int toColor = WXResourceUtils.getColor(WXUtils.getString(value, null), 0); if (WXViewUtils.getBorderDrawable(taregtView) != null) { fromColor = WXViewUtils.getBorderDrawable(taregtView).getColor(); } else if (taregtView.getBackground() instanceof ColorDrawable) { fromColor = ((ColorDrawable) taregtView.getBackground()).getColor(); } holders.add(PropertyValuesHolder.ofObject(new BackgroundColorProperty(), new ArgbEvaluator(), fromColor, toColor)); } break; default: break; } } if (token == lockToken.get()) { transformPendingUpdates.clear(); } transformAnimator = ObjectAnimator.ofPropertyValuesHolder(taregtView, holders.toArray(new PropertyValuesHolder[holders.size()])); transformAnimator.setDuration((long) duration); if ((long) delay > 0) { transformAnimator.setStartDelay((long) delay); } if (interpolator != null) { transformAnimator.setInterpolator(interpolator); } transformAnimator.addListener(new AnimatorListenerAdapter() { boolean hasCancel = false; @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); hasCancel = true; } @Override public void onAnimationEnd(Animator animation) { if (hasCancel) { return; } super.onAnimationEnd(animation); WXTransition.this.onTransitionAnimationEnd(); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("WXTransition transform onTransitionAnimationEnd " + domObject.getRef()); } } }); transformAnimator.start(); } public void doPendingLayoutAnimation() { if (layoutValueAnimator != null) { layoutValueAnimator.cancel(); layoutValueAnimator = null; } if (layoutPendingUpdates.size() == 0) { return; } PropertyValuesHolder[] holders = new PropertyValuesHolder[layoutPendingUpdates.size()]; int index = 0; for (String property : properties) { if (!LAYOUT_PROPERTIES.contains(property)) { continue; } if (layoutPendingUpdates.containsKey(property)) { Object targetValue = layoutPendingUpdates.remove(property); synchronized (targetStyles) { targetStyles.put(property, targetValue); } holders[index] = createLayoutPropertyValueHolder(property, targetValue); index++; } } layoutPendingUpdates.clear(); doLayoutPropertyValuesHolderAnimation(holders); } private PropertyValuesHolder createLayoutPropertyValueHolder(String property, Object value) { PropertyValuesHolder holder = null; switch (property) { case Constants.Name.WIDTH: { holder = PropertyValuesHolder.ofFloat(Constants.Name.WIDTH, domObject.getLayoutWidth(), WXViewUtils.getRealPxByWidth(WXUtils.getFloat(value, 0.0f), domObject.getViewPortWidth())); } break; case Constants.Name.HEIGHT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.HEIGHT, domObject.getLayoutHeight(), WXViewUtils.getRealPxByWidth(WXUtils.getFloat(value, 0.0f), domObject.getViewPortWidth())); } break; case Constants.Name.MARGIN_TOP: { holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_TOP, domObject.getMargin().get(Spacing.TOP), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.MARGIN_LEFT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_LEFT, domObject.getMargin().get(Spacing.LEFT), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.MARGIN_RIGHT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_RIGHT, domObject.getMargin().get(Spacing.RIGHT), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.MARGIN_BOTTOM: { holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_BOTTOM, domObject.getMargin().get(Spacing.BOTTOM), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.LEFT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.LEFT, domObject.getPositionLeft(), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.RIGHT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.RIGHT, domObject.getPositionRight(), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.BOTTOM: { holder = PropertyValuesHolder.ofFloat(Constants.Name.BOTTOM, domObject.getPositionBottom(), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.TOP: { holder = PropertyValuesHolder.ofFloat(Constants.Name.TOP, domObject.getPositionTop(), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.PADDING_TOP: { holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_TOP, domObject.getPadding().get(Spacing.TOP), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.PADDING_BOTTOM: { holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_BOTTOM, domObject.getPadding().get(Spacing.BOTTOM), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.PADDING_LEFT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.TOP, domObject.getPadding().get(Spacing.LEFT), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; case Constants.Name.PADDING_RIGHT: { holder = PropertyValuesHolder.ofFloat(Constants.Name.TOP, domObject.getPadding().get(Spacing.RIGHT), WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, domObject.getViewPortWidth()), domObject.getViewPortWidth())); } break; default: break; } if (holder == null) { holder = PropertyValuesHolder.ofFloat(property, 1, 1); } return holder; } private void doLayoutPropertyValuesHolderAnimation(PropertyValuesHolder[] holders) { layoutValueAnimator = ValueAnimator.ofPropertyValuesHolder(holders); layoutValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { PropertyValuesHolder holders[] = animation.getValues(); for (PropertyValuesHolder holder : holders) { String property = holder.getPropertyName(); switch (property) { case Constants.Name.WIDTH: { domObject.setStyleWidth((Float) animation.getAnimatedValue(property)); } break; case Constants.Name.HEIGHT: { domObject.setStyleHeight((Float) animation.getAnimatedValue(property)); } break; case Constants.Name.MARGIN_TOP: { domObject.setMargin(Spacing.TOP, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.MARGIN_LEFT: { domObject.setMargin(Spacing.LEFT, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.MARGIN_RIGHT: { domObject.setMargin(Spacing.RIGHT, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.MARGIN_BOTTOM: { domObject.setMargin(Spacing.BOTTOM, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.LEFT: { domObject.setPositionLeft((Float) animation.getAnimatedValue(property)); } break; case Constants.Name.RIGHT: { domObject.setPositionRight((Float) animation.getAnimatedValue(property)); } break; case Constants.Name.BOTTOM: { domObject.setPositionBottom((Float) animation.getAnimatedValue(property)); } break; case Constants.Name.TOP: { domObject.setPositionTop((Float) animation.getAnimatedValue(property)); } break; case Constants.Name.PADDING_TOP: { domObject.setPadding(Spacing.TOP, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.PADDING_BOTTOM: { domObject.setPadding(Spacing.BOTTOM, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.PADDING_LEFT: { domObject.setPadding(Spacing.LEFT, (Float) animation.getAnimatedValue(property)); } break; case Constants.Name.PADDING_RIGHT: { domObject.setPadding(Spacing.RIGHT, (Float) animation.getAnimatedValue(property)); } break; default: break; } } DOMActionContext domActionContext = WXSDKManager.getInstance().getWXDomManager() .getDomContext(domObject.getDomContext().getInstanceId()); if (domActionContext == null) { return; } domActionContext.markDirty(); WXSDKManager.getInstance().getWXDomManager() .sendEmptyMessageDelayed(WXDomHandler.MsgType.WX_DOM_TRANSITION_BATCH, 0); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("WXTransition send layout batch msg"); } } }); layoutValueAnimator.addListener(new AnimatorListenerAdapter() { boolean hasCancel = false; @Override public void onAnimationCancel(Animator animation) { super.onAnimationCancel(animation); hasCancel = true; } @Override public void onAnimationEnd(Animator animation) { if (hasCancel) { return; } super.onAnimationEnd(animation); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("WXTransition layout onTransitionAnimationEnd " + domObject.getRef()); } WXTransition.this.onTransitionAnimationEnd(); } }); if (interpolator != null) { layoutValueAnimator.setInterpolator(interpolator); } layoutValueAnimator.setStartDelay((long) (delay)); layoutValueAnimator.setDuration((long) (duration)); layoutValueAnimator.start(); } private synchronized void onTransitionAnimationEnd() { if (duration > 0) { if (transitionEndEvent != null) { View view = getTargetView(); if (view != null && transitionEndEvent != null) { view.post(transitionEndEvent); } transitionEndEvent = null; } } synchronized (targetStyles) { if (targetStyles.size() > 0) { WXComponent component = getComponent(); for (String property : properties) { if (targetStyles.containsKey(property)) { Object targetValue = targetStyles.remove(property); domObject.getStyles().put(property, targetValue); if (component != null && component.getDomObject() != null) { component.getDomObject().getStyles().put(property, targetValue); } } } targetStyles.clear(); } } } private WXComponent getComponent() { DOMActionContext domActionContext = WXSDKManager.getInstance().getWXDomManager() .getDomContext(domObject.getDomContext().getInstanceId()); if (domActionContext != null) { WXComponent component = domActionContext.getCompByRef(domObject.getRef()); return component; } return null; } private View getTargetView() { if (domObject.getDomContext() == null) { return null; } DOMActionContext domActionContext = WXSDKManager.getInstance().getWXDomManager() .getDomContext(domObject.getDomContext().getInstanceId()); if (domActionContext != null) { WXComponent component = domActionContext.getCompByRef(domObject.getRef()); if (component != null && component.getHostView() != null) { return component.getHostView(); } } return null; } /** * get time millis * */ private static long parseTimeMillis(Map<String, Object> style, String key, long defaultValue) { String duration = WXUtils.getString(style.get(key), null); if (duration != null) { duration = duration.replaceAll("ms", ""); } if (TextUtils.isEmpty(duration)) { return defaultValue; } try { return (long) Float.parseFloat(duration); } catch (NumberFormatException e) { return defaultValue; } } /** * create interpolcator same with web * http://www.w3school.com.cn/cssref/pr_transition-timing-function.asp * */ private static Interpolator createTimeInterpolator(String interpolator) { if (!TextUtils.isEmpty(interpolator)) { switch (interpolator) { case EASE_IN: return PathInterpolatorCompat.create(0.42f, 0f, 1f, 1f); case EASE_OUT: return PathInterpolatorCompat.create(0f, 0f, 0.58f, 1f); case EASE_IN_OUT: return PathInterpolatorCompat.create(0.42f, 0f, 0.58f, 1f); case EASE: return PathInterpolatorCompat.create(0.25f, 0.1f, 0.25f, 1f); case LINEAR: return PathInterpolatorCompat.create(0.0f, 0f, 1f, 1f); default: try { //Parse cubic-bezier SingleFunctionParser<Float> parser = new SingleFunctionParser<>(interpolator, new SingleFunctionParser.FlatMapper<Float>() { @Override public Float map(String raw) { return Float.parseFloat(raw); } }); List<Float> params = parser.parse(CUBIC_BEZIER); if (params != null && params.size() == 4) { return PathInterpolatorCompat.create(params.get(0), params.get(1), params.get(2), params.get(3)); } } catch (RuntimeException e) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.e("WXTransition", e); } } } } return PathInterpolatorCompat.create(0.25f, 0.1f, 0.25f, 1f); } private static void updateTransitionProperties(WXTransition transition, String transtionProperty) { if (transtionProperty == null) { return; } transition.properties.clear(); String[] propertiesArray = PROPERTY_SPLIT_PATTERN.split(transtionProperty); for (String property : propertiesArray) { String trim = property.trim(); if (TextUtils.isEmpty(trim)) { continue; } if (!(LAYOUT_PROPERTIES.contains(trim) || TRANSFORM_PROPERTIES.contains(trim))) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.e("WXTransition Property Not Supported" + trim + " in " + transtionProperty); } continue; } transition.properties.add(trim); } } }