Weex Android SDK源码分析之Module(animation)
2016-06-23 15:33
567 查看
前言
module 怎能少得了动画呢~代码解读
weex codeAPI 接口 transition(node, options, callback)
Arguments 参数 node(Node):将要动画的元素。 options(object):操作选项 styles(object):指定要应用的过渡效果的样式的名称和值。 color(string):色彩的元素时,animaiton完成。 transform(object):变换函数被应用到元素。支持下列值。 translate/ translatex / translatey(字符串):translate的元素到新的位置。该值可以是像素或百分比 rotate(string):单位为度。 scale(string):放大或缩小元素。 duration(number):指定一个过渡动画需要完成的毫秒数。默认情况下,该值是毫秒,这意味着没有动画会发生。 timingfuncion(string):用来描述的方式被过渡效果影响的中间值的计算。默认值为 linear, 也可以是一个 ease-in, ease-out, ease-in-out, linear or cubic-bezier(x1, y1, x2, y2). delay(number):指定一个变化的要求,是转变和过渡效果的启动性能之间等待的毫秒数。默认情况下,该值为0毫秒。 transform-origin(string):尺度和旋转的中心。该值可以是x、y的像素或关键字,如 left, right, bottom, top, center。 Callback 过渡完成后调用的回调回调函数。
Example 实例 <template> <div class="ct"> <div id="test"></div> </div> </template> <script> module.exports = { ready: function () { var animation = require('@weex-module/animation'); var testEl = this.$el('test'); animation.transition(testEl, { styles: { color: '#FF0000', transform: 'translate(1, 1)' }, duration: 0, //ms timingFunction: 'ease', transform-origin: 'center center', delay: 0 //ms }, function () { nativeLog('animation finished.') }) } } </script>
android code
一、注册
WXModuleManager.registerModule("animation", WXAnimationModule.class, true);
二、动画WXAnimationBean类
看的出来与weex定义的options 一一对应
public class WXAnimationBean { public final static String LINEAR = "linear"; public final static String EASE_IN_OUT = "ease-in-out"; public final static String EASE_IN = "ease-in"; public final static String EASE_OUT = "ease-out"; public long delay;// 延迟时间 public long duration; // 显示时间 public String timingFunction; // 过渡效果 public Style styles; //样式 public static class Style { // 支持动画样式 public final static String ANDROID_TRANSLATION_X = "translationX"; public final static String ANDROID_TRANSLATION_Y = "translationY"; public final static String ANDROID_ROTATION = "rotation"; public final static String ANDROID_SCALE_X = "scaleX"; public final static String ANDROID_SCALE_Y = "scaleY"; public final static String WX_TRANSLATE = "translate"; public final static String WX_TRANSLATE_X = "translateX"; public final static String WX_TRANSLATE_Y = "translateY"; public final static String WX_ROTATE = "rotate"; public final static String WX_SCALE_X = "scaleX"; public final static String WX_SCALE_Y = "scaleY"; public final static String WX_SCALE = "scale"; public final static String ALPHA = "alpha"; public final static String BACKGROUND_COLOR = "backgroundColor"; public final static String TOP = "top"; public final static String BOTTOM = "bottom"; public final static String RIGHT = "right"; public final static String LEFT = "left"; public final static String CENTER = "center"; // 动画 key map public static Map<String, List<String>> wxToAndroidMap = new HashMap<>(); static { wxToAndroidMap.put(WX_TRANSLATE, Arrays.asList (ANDROID_TRANSLATION_X, ANDROID_TRANSLATION_Y)); wxToAndroidMap.put(WX_TRANSLATE_X, Collections.singletonList(ANDROID_TRANSLATION_X)); wxToAndroidMap.put(WX_TRANSLATE_Y, Collections.singletonList(ANDROID_TRANSLATION_Y)); wxToAndroidMap.put(WX_ROTATE, Collections.singletonList(ANDROID_ROTATION)); wxToAndroidMap.put(WX_SCALE, Arrays.asList(ANDROID_SCALE_X, ANDROID_SCALE_Y)); wxToAndroidMap.put(WX_SCALE_X, Collections.singletonList(ANDROID_SCALE_X)); wxToAndroidMap.put(WX_SCALE_Y, Collections.singletonList(ANDROID_SCALE_Y)); wxToAndroidMap = Collections.unmodifiableMap(wxToAndroidMap); } public String opacity; // 透明度 public String backgroundColor; //背景 public String transform;// 动画效果 public String transformOrigin;//过渡效果 private Map<String, Float> transformMap = new HashMap<>(); // 动画map private Pair<Float, Float> pivot; // 中心点坐标 public Map<String, Float> getTransformMap() { return transformMap; } public void setTransformMap(Map<String, Float> transformMap) { this.transformMap = transformMap; } public Pair<Float, Float> getPivot() { return pivot; } public void setPivot(Pair<Float, Float> pivot) { this.pivot = pivot; } } }
二、WXModuleManager 部分方法
public class WXAnimationModule extends WXModule { // 添加动画样式 public static void applyTransformStyle(Map<String, Object> style, WXComponent component) { if (component != null) { View target = component.getRealView(); if (style != null && target != null) { Object transform = style.get("transform"); if (transform instanceof String && !TextUtils.isEmpty((String) transform) && target.getLayoutParams() != null) { String transformOrigin; try { transformOrigin = (String) component.mDomObj.style.get("transformOrigin"); } catch (NullPointerException e) { transformOrigin = null; } WXAnimationBean animationBean = new WXAnimationBean(); animationBean.styles = new WXAnimationBean.Style(); animationBean.styles.setPivot( WXAnimationModule.parsePivot(transformOrigin, target.getLayoutParams())); animationBean.styles.setTransformMap( WXAnimationModule.parseTransForm((String) transform, target.getLayoutParams())); Animator animator = WXAnimationModule.createAnimator(animationBean, target); if (animator != null) { animator.setDuration(0); animator.start(); } } } } } // ** 解析动画bean public static WXAnimationBean parseAnimation(@Nullable String animation, ViewGroup.LayoutParams layoutParams) { try { WXAnimationBean animationBean = JSONObject.parseObject(animation, WXAnimationBean.class); if (animationBean != null && animationBean.styles != null) { WXAnimationBean.Style style = animationBean.styles; style.setTransformMap(parseTransForm(style.transform, layoutParams)); style.setPivot(parsePivot(style.transformOrigin, layoutParams)); } return animationBean; } catch (RuntimeException e) { WXLogUtils.e(WXLogUtils.getStackTrace(e)); return null; } } // ** 创建动画 public static ObjectAnimator createAnimator(@NonNull WXAnimationBean animation, @NonNull View target) { WXAnimationBean.Style style = animation.styles; if (style != null) { ObjectAnimator animator; List<PropertyValuesHolder> holders = new LinkedList<>(); if (style.getTransformMap() != null) { if (style.getTransformMap().isEmpty()) { holders.addAll(moveBackToOrigin()); } else { for (Map.Entry<String, Float> entry : style.getTransformMap().entrySet()) { holders.add(PropertyValuesHolder.ofFloat(entry.getKey(), entry.getValue())); } } } if (!TextUtils.isEmpty(style.opacity)) { holders.add(PropertyValuesHolder.ofFloat(WXAnimationBean.Style.ALPHA, Float.valueOf(style.opacity))); } if (!TextUtils.isEmpty(style.backgroundColor)) { if (target.getBackground() instanceof WXBackgroundDrawable) { holders.add(PropertyValuesHolder.ofObject( WXAnimationBean.Style.BACKGROUND_COLOR, new ArgbEvaluator(), ((WXBackgroundDrawable) target.getBackground()).getColor(), WXResourceUtils.getColor(style.backgroundColor))); } else if (target.getBackground() instanceof ColorDrawable) { holders.add(PropertyValuesHolder.ofObject( WXAnimationBean.Style.BACKGROUND_COLOR, 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(animation.delay); return animator; } else { return null; } } // ** 创建动画监听器 public static Animator.AnimatorListener createAnimatorListener (final WXSDKInstance mWXSDKInstance, final String callBack) { if (!TextUtils.isEmpty(callBack)) { return new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (mWXSDKInstance == null) { WXLogUtils.e("WXRenderStatement-onAnimationEnd mWXSDKInstance == null NPE"); } else { // ** 动画结束后回调 WXSDKManager.getInstance().callback(mWXSDKInstance.getInstanceId(), callBack, new HashMap<String, Object>()); } } }; } else { return null; } } // 创建动画过去效果 public static 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(); } } return null; } // 解析动画 private static Map<String, Float> parseTransForm (String rawTransform, final ViewGroup.LayoutParams layoutParams) { if (!TextUtils.isEmpty(rawTransform)) { FunctionParser<Float> parser = new FunctionParser<> (rawTransform, new FunctionParser.Mapper<Float>() { @Override public Map<String, Float> map(String functionName, List<String> raw) { Map<String, Float> result = new HashMap<>(); if (raw != null && !raw.isEmpty()) { // 判断是否有此类型动画 if (WXAnimationBean.Style.wxToAndroidMap.containsKey(functionName)) { // 根据参数转换动画 result.putAll(convertParam( layoutParams, WXAnimationBean.Style.wxToAndroidMap.get(functionName), raw)); } } return result; } }); return parser.parse(); } return new LinkedHashMap<>(); } private static String parsePercentOrPx(String raw, int unit) { String lower = raw.toLowerCase(); if (lower.endsWith("%")) { return parsePercent(raw, unit); } else if (lower.endsWith("px")) { return Float.toString( WXViewUtils.getRealPxByWidth(Float.parseFloat(raw.replace("px", "")))); } return Float.toString(WXViewUtils.getRealPxByWidth(Float.parseFloat(raw))); } // 按百分比进行转化 private static String parsePercent(String percent, int unit) { return Float.toString(Float.parseFloat(percent.replace("%", "")) / 100 * unit); } // 解析坐标 private static Pair<Float, Float> parsePivot(@Nullable String transformOrigin, ViewGroup.LayoutParams layoutParams) { String[] split; List<String> list; if (!TextUtils.isEmpty(transformOrigin) && ((split = transformOrigin.split("\\s+")).length >= 2)) { list = Arrays.asList(split).subList(0, 2); return parsePivot(list, layoutParams); } else { return parsePivot( Arrays.asList(WXAnimationBean.Style.CENTER, WXAnimationBean.Style.CENTER), layoutParams); } } private static Pair<Float, Float> parsePivot (@NonNull List<String> list, ViewGroup.LayoutParams layoutParams) { return new Pair<>(parsePivotX(list.get(0), layoutParams), parsePivotY(list.get(1), layoutParams)); } private static float parsePivotX(String x, ViewGroup.LayoutParams layoutParams) { String value = x; if (TextUtils.equals(x, WXAnimationBean.Style.LEFT)) { value = "0%"; } else if (TextUtils.equals(x, WXAnimationBean.Style.RIGHT)) { value = "100%"; } else if (TextUtils.equals(x, WXAnimationBean.Style.CENTER)) { value = "50%"; } value = parsePercentOrPx(value, layoutParams.width); return Float.parseFloat(value); } private static float parsePivotY(String y, ViewGroup.LayoutParams layoutParams) { String value = y; if (TextUtils.equals(y, WXAnimationBean.Style.TOP)) { value = "0%"; } else if (TextUtils.equals(y, WXAnimationBean.Style.BOTTOM)) { value = "100%"; } else if (TextUtils.equals(y, WXAnimationBean.Style.CENTER)) { value = "50%"; } value = parsePercentOrPx(value, layoutParams.height); return Float.parseFloat(value); } // 参数转化 private static Map<String, Float> convertParam(ViewGroup.LayoutParams layoutParams, @NonNull List<String> nameSet, @NonNull List<String> rawValue) { Map<String, Float> result = new HashMap<>(); List<String> convertedList = new ArrayList<>(); if (nameSet.contains(WXAnimationBean.Style.ANDROID_ROTATION)) { convertedList.addAll(parseRotation(rawValue)); } else if (nameSet.contains(WXAnimationBean.Style.ANDROID_TRANSLATION_X) || nameSet.contains(WXAnimationBean.Style.ANDROID_TRANSLATION_Y)) { convertedList.addAll(parseTranslation(nameSet, layoutParams, rawValue)); } else if (nameSet.contains(WXAnimationBean.Style.ANDROID_SCALE_X) || nameSet.contains(WXAnimationBean.Style.ANDROID_SCALE_Y)) { convertedList.addAll(parseScale(nameSet.size(), rawValue)); } if (nameSet.size() == convertedList.size()) { for (int i = 0; i < nameSet.size(); i++) { result.put(nameSet.get(i), Float.parseFloat(convertedList.get(i))); } } return result; } private static List<String> parseTranslation (List<String> nameSet, ViewGroup.LayoutParams layoutParams,@NonNull List<String> rawValue) { List<String> convertedList = new ArrayList<>(); String first = rawValue.get(0); if (nameSet.size() == 1) { parseSingleTranslation(nameSet, layoutParams, convertedList, first); } else { parseDoubleTranslation(layoutParams, rawValue, convertedList, first); } return convertedList; } private static void parseSingleTranslation (List<String> nameSet, ViewGroup.LayoutParams layoutParams, List<String> convertedList, String first) { if (nameSet.contains(WXAnimationBean.Style.ANDROID_TRANSLATION_X)) { convertedList.add(parsePercentOrPx(first, layoutParams.width)); } else if (nameSet.contains(WXAnimationBean.Style.ANDROID_TRANSLATION_Y)) { convertedList.add(parsePercentOrPx(first, layoutParams.height)); } } private static void parseDoubleTranslation(ViewGroup.LayoutParams layoutParams, @NonNull List<String> rawValue, List<String> convertedList, String first) { String second; if (rawValue.size() == 1) { second = first; } else { second = rawValue.get(1); } if (layoutParams != null) { convertedList.add(parsePercentOrPx(first, layoutParams.width)); convertedList.add(parsePercentOrPx(second, layoutParams.height)); } } // 解析缩放 private static List<String> parseScale(int size, @NonNull List<String> rawValue) { List<String> convertedList = new ArrayList<>(); convertedList.addAll(rawValue); if (size != 1 && rawValue.size() == 1) { convertedList.addAll(rawValue); } return convertedList; } // 解析旋转 private static List<String> parseRotation(@NonNull List<String> rawValue) { List<String> convertedList = new ArrayList<>(); String lower; for (String raw : rawValue) { lower = raw.toLowerCase(); if (lower.endsWith("deg")) { convertedList.add(lower.replace("deg", "")); } else { convertedList.add( Double.valueOf(Math.toDegrees(Double.parseDouble(raw))).toString()); } } return convertedList; } private static List<PropertyValuesHolder> moveBackToOrigin() { List<PropertyValuesHolder> holders = new LinkedList<>(); holders.add(PropertyValuesHolder.ofFloat(WXAnimationBean.Style.ANDROID_TRANSLATION_X, 0)); holders.add(PropertyValuesHolder.ofFloat(WXAnimationBean.Style.ANDROID_TRANSLATION_Y, 0)); holders.add(PropertyValuesHolder.ofFloat(WXAnimationBean.Style.ANDROID_SCALE_X, 1)); holders.add(PropertyValuesHolder.ofFloat(WXAnimationBean.Style.ANDROID_SCALE_Y, 1)); holders.add(PropertyValuesHolder.ofFloat(WXAnimationBean.Style.ANDROID_ROTATION, 0)); return holders; } // 接受weex桥接回调方法 @WXModuleAnno public void transition(String ref, String animation, String callBack) { WXSDKManager.getInstance().getWXRenderManager(). startAnimation(mWXSDKInstance.getInstanceId(), ref, animation, callBack); } }
三、渲染管理器(WXRenderManager)
// 渲染声明队列 private ConcurrentHashMap<String, WXRenderStatement> mRegistries; // 从登记的渲染声明中拿到当前的需要执行动画的animation控件,开启动画 public void startAnimation(String instanceId, String ref, String animation, String callBack) { WXRenderStatement statement = mRegistries.get(instanceId); if (statement == null) { return; } statement.startAnimation(ref, animation, callBack); }
四、渲染声明(WXRenderStatement)
// 存储组件map private Map<String, WXComponent> mRegistry; // 从map中取到相应控件 void startAnimation(String ref, String animation, String callBack) { WXComponent component = mRegistry.get(ref); if (component == null || component.getRealView() == null) { return; } try { // 获取 animationBean WXAnimationBean animationBean = WXAnimationModule.parseAnimation(animation, component.getRealView().getLayoutParams()); if (animationBean != null) { // 创建 animator 对象 Animator animator = WXAnimationModule.createAnimator(animationBean, component.getRealView()); if (animator != null) { // 创建监听 Animator.AnimatorListener animatorListener = WXAnimationModule.createAnimatorListener(mWXSDKInstance, callBack); // 创建插补器 (过渡效果) Interpolator interpolator = WXAnimationModule.createTimeInterpolator(animationBean); if (animatorListener != null) { animator.addListener(animatorListener); } if (interpolator != null) { animator.setInterpolator(interpolator); } animator.setDuration(animationBean.duration); animator.start(); } } } catch (RuntimeException e) { WXLogUtils.e(WXLogUtils.getStackTrace(e)); } }
流程 :
1、js 调用 本地module方法进行绘制; 2、调用渲染声明生成动画bean; 3、创建动画、设置监听、创建过渡效果; 4、开启动画,结束回调callback;
相关文章推荐
- android颜色对应的xml配置值,颜色表
- Android的Notification应用详解和PendingIntent的初步剖解
- android是32-bit系统还是64-bit系统
- Android 属性动画(一)
- ItemTouchHelper源码解析
- android --APK下载、安装、启动
- Android常用框架混淆代码
- Android——软键盘操作+fragment之间传递参数+TextView限制字数,显示"..."
- Android SDK下载和更新
- 自定义Switch 控件textOn和textOff字体大小
- Android内容提供者(二)创建自己的Provider
- 解决启动Android应用程序时出现白屏或者黑屏的问题
- android 获取手机联系人信息
- Android studio 开发者信息设置
- android TextView 垂直自动滚动
- Android中正确保存view的状态
- android studio 快捷键修改
- Android单位
- Android工具类系列-BitmapUtil
- 学习心得(Android之WakeLock机制分析)