首页 > 代码库 > 自定义res/anim加载类,加载自定义Interpolator

自定义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: flashInterpolator12-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,到此结束了。

 
 

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