自定义res/anim加载类,加载自定义Interpolator

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

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

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

例如:

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



这个插值器只是改变了动画的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文件中通过android:interpolator属性引用该文件:

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




    <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" >
    

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.(Animation.java:255)
12-09 05:11:41.144: E/AndroidRuntime(3649):     at android.view.animation.AlphaAnimation.(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


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,到此结束了。

 
 

转载于:https://www.cnblogs.com/emmet7life/p/interpolator.html

你可能感兴趣的:(自定义res/anim加载类,加载自定义Interpolator)