zoukankan      html  css  js  c++  java
  • 自定义res/anim加载类,加载自定义Interpolator

    上一篇文章 原文翻译 Android_Develop_API Guides_Animation Resources(动画资源)

    介绍了Android中的动画资源,里面有一个章节是讲如何自定义插值器(Custom interpolators)的。

    但是当前Android只为我们提供了自定义基于现有插值器的部分定制,只能修改当前要被修改的插值器所支持的属性。

    例如:

    文件位置:res/anim/my_overshoot_interpolator.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <overshootInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
        android:tension="7.0" />
    

    这个插值器只是改变了<overshootInterpolator>动画的tension属性,将默认的2改成了7。

    我之前试着写了一个一个自定义插值器XML文件:

    对应Java类文件:com.and.resource.anim.FlashInterpolator.java

    
    
    package com.and.resource.anim;
    
    import android.view.animation.Interpolator;

    /**
    * 自定义插入帧.(实现Interpolator接口) * * @author chenjl * */ public class FlashInterpolator implements Interpolator { @Override public float getInterpolation(float input) { if (input < 0.5f) { return 0; } else { return 1; } } }

    在anim下创建了该插值器类对应的文件,文件位置:res/anim/flash_interpolator.xml

    <?xml version="1.0" encoding="utf-8"?>
    <flashInterpolator />

    然后在我的动画XML文件中通过android:interpolator属性引用该文件:

    文件位置:res/anim/my_animation.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <alpha
            android:interpolator="@anim/flash_interpolator"
            android:duration="4000"
            android:fillAfter="true"
            android:fromAlpha="1"
            android:repeatCount="infinite"
            android:repeatMode="restart"
            android:toAlpha="0.01" >
        </alpha>
    
    </set>

    Okay,最后,我像加载普通的anim动画XML文件一样,调用

    AnimationUtils.loadAnimation(this, R.anim.my_animation);

    来加载这个动画。但是,报错了,错误信息如下:

    12-09 05:11:41.144: E/AndroidRuntime(3649): Caused by: java.lang.RuntimeException: Unknown interpolator name: flashInterpolator
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AnimationUtils.createInterpolatorFromXml(AnimationUtils.java:328)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AnimationUtils.loadInterpolator(AnimationUtils.java:271)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.Animation.setInterpolator(Animation.java:391)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.Animation.<init>(Animation.java:255)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AlphaAnimation.<init>(AlphaAnimation.java:40)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AnimationUtils.createAnimationFromXml(AnimationUtils.java:116)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AnimationUtils.createAnimationFromXml(AnimationUtils.java:114)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AnimationUtils.createAnimationFromXml(AnimationUtils.java:91)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AnimationUtils.loadAnimation(AnimationUtils.java:72)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at com.and.resource.MainActivity.onCreate(MainActivity.java:25)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.app.Activity.performCreate(Activity.java:5231)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
    12-09 05:11:41.144: E/AndroidRuntime(3649):     ... 11 more

    跟着错误日记,进入相关源码查看报错位置。

    AnimationUtils类中createInterpolatorFromXml(Context c, XmlPullParser parser)方法的源代码:

    private static Interpolator createInterpolatorFromXml(Context c, XmlPullParser parser)
                throws XmlPullParserException, IOException {
            
            Interpolator interpolator = null;
     
            // Make sure we are on a start tag.
            int type;
            int depth = parser.getDepth();
    
            while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
                   && type != XmlPullParser.END_DOCUMENT) {
    
                if (type != XmlPullParser.START_TAG) {
                    continue;
                }
    
                AttributeSet attrs = Xml.asAttributeSet(parser);
                
                String  name = parser.getName();
        
                
                if (name.equals("linearInterpolator")) {
                    interpolator = new LinearInterpolator(c, attrs);
                } else if (name.equals("accelerateInterpolator")) {
                    interpolator = new AccelerateInterpolator(c, attrs);
                } else if (name.equals("decelerateInterpolator")) {
                    interpolator = new DecelerateInterpolator(c, attrs);
                }  else if (name.equals("accelerateDecelerateInterpolator")) {
                    interpolator = new AccelerateDecelerateInterpolator(c, attrs);
                }  else if (name.equals("cycleInterpolator")) {
                    interpolator = new CycleInterpolator(c, attrs);
                } else if (name.equals("anticipateInterpolator")) {
                    interpolator = new AnticipateInterpolator(c, attrs);
                } else if (name.equals("overshootInterpolator")) {
                    interpolator = new OvershootInterpolator(c, attrs);
                } else if (name.equals("anticipateOvershootInterpolator")) {
                    interpolator = new AnticipateOvershootInterpolator(c, attrs);
                } else if (name.equals("bounceInterpolator")) {
                    interpolator = new BounceInterpolator(c, attrs);
                } else {
                    throw new RuntimeException("Unknown interpolator name: " + parser.getName());
                }
    
            }
        
            return interpolator;
    
        }

    我很快发现,这里的if判断,根本没有考虑其他的自定义插值器类,如果遇到未知的,会直接抛出运行时错误。

    那为什么要把插值器接口开放呢!!!?好吧,答案是,的确可以通过代码来使用:

    ImageView myImage = (ImageView) findViewById(R.id.img_anim_test);
    Animation myAnim = AnimationUtils.loadAnimation(this, R.anim.my_animation);
    FlashInterpolator myInterpolator = new FlashInterpolator();
    myAnim.setInterpolator(myInterpolator);
    myImage.startAnimation(myAnim);

    但我们可以改造下这个AnimationUtils -> createInterpolatorFromXml(Context c, XmlPullParser parser) 方法。定义一个类,例如,OptAnimationUtils,然后将AnimationUtils -> createInterpolatorFromXml(Context c, XmlPullParser parser)中的源码拷贝过来,将方法中的:

    else {
      throw new RuntimeException("Unknown interpolator name: " + parser.getName());
    }

    改成:

    else {
      try {
        interpolator = (Interpolator) Class.forName(name).getConstructor(Context.class, AttributeSet.class).newInstance(c, attrs);
      } catch (Exception te) {
        throw new RuntimeException("Unknown interpolator name: " + parser.getName() + " error:" + te.getMessage());
      }
    }

    当然,你需要把Interpolator loadInterpolator(Context context, int id)方法也拷贝进来。

    然后,这么使用:

    1)修改 res/anim/flash_interpolator.xml

    <?xml version="1.0" encoding="utf-8"?>
    <com.and.resource.anim.FlashInterpolator />

    2)修改com.and.resource.anim.FlashInterpolator,添加构造方法:

    package com.and.resource.anim;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.animation.Interpolator;
    
    /**
     * 自定义插入帧.(实现Interpolator接口)
     * 
     * @author chenjl
     * 
     */
    public class FlashInterpolator implements Interpolator
    {
        
        public FlashInterpolator() {
            
        }
        
        public FlashInterpolator(Context context, AttributeSet attrs) {
            
        }
    
        @Override
        public float getInterpolation(float input)
        {
            if (input < 0.5f) {
                return 0;
            } else {
                return 1;
            }
        }
    
    }

    3)代码中使用:

    myAnim.setInterpolator(OptAnimationUtils.loadInterpolator(this, R.anim.flash_interpolator));

    目前,我们仍然无法将这个自定义的插值器在某个动画XML中直接通过android:interpolator属性来引用,因为我们的程序无法修改系统的源码。

    貌似这个方法很鸡肋啊,因为我们只需要在代码中new出插值器类,然后使用Animation.setInterpolator(Interpolator in);设置即可,上面的方法貌似多了许多步骤。

    但是,这里只是告诉大家一个方法,如果你自定义了Animation同时自定义了插值器,那么这个方法就允许你直接将插值器通过android:interpolator直接放置在XML动画资源中了。

    如果是大批量的动画文件,那不是很有用嘛。^_^

    Okay,到此结束了。

     
     
  • 相关阅读:
    JS中的一些函数式编程术语
    学习RxJS:Cycle.js
    学习RxJS: 导入
    爬虫的终极形态:nightmare
    RxJS/Cycle.js 与 React/Vue 相比更适用于什么样的应用场景?
    [转]Shared——RN如何实现一个ExpandableList(可展开列表)组件
    Algorithm——ZigZag Conversion
    Review——RN视图缩放框架react-native-view-transformer解析
    Between Worlds 3 太阳与地球
    Tips——RN构造函数内绑定导致页面交互卡顿
  • 原文地址:https://www.cnblogs.com/emmet7life/p/interpolator.html
Copyright © 2011-2022 走看看