已阅读:15,500 次
Android自定义Animation动画
ian | Android | 2012/06/06


产品需求中有一个动画效果:TextureView按照曲线路径进行移动,并且同时做Scale和Alpha变化。当然,同时
进行多个动画只需要使用AnimationSet就行了,但是对于”曲线”这样的移动效果,只能采用多个TranslateAnimation来顺序进行以模拟曲线的效果了。这样的实现方式没有问题,但是仔细观察实际效果却发现,在连续两个动画进行过程中,第一个动画结束到第二个动画开始,TextureView会出现瞬间的停止,然后才会进行第二个动画,也就是会”卡”一下,这样的效果就不太理想了。

没办法,只能实现一个自定义动画,即在一个Animation中实现View的曲线移动。

浏览一下Animation的代码,可以发现有applyTransformation这样一个接口,这个接口的作用就是提供一个转换矩阵来对View进行变换,矩阵变换的原理在以前介绍的图像处理的文章中介绍过,这里就不再赘述,而且我们需要使用的Matrix的功能:如位移、大小等,都有响应的接口可以直接使用,很方便。

下面看看一个简单的自定义动画的实现:为了方便定义动画的状态(也可以成为”关键帧”),我们简单定义一个类:

1
2
3
4
5
6
7
8
9
public class AnimationStatus 
{
    public float X = 0;
    public float Y = 0;
    public float Scale_X = 1;
    public float Scale_Y = 1;
    public float Alpha = 1;
    public float Time = 0;
}

然后是继承自Animation的ExtAnimation对象,主要的接口和代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class ExtAnimation extends Animation
{
	// 保存动画的关键帧
	private List<AnimationStatus> mAniList = new ArrayList<AnimationStatus>();
 
	public ViewSwitchAnimation() 
	{
            this.setFillAfter(true);
            this.setFillEnabled(true);
            this.setDuration(1000);
        }
 
	// 添加一个关键帧(添加时Status请按Time排好序)
        public void addStatus(AnimationStatus status, float timepoint) 
	{
            keyStatus.Time = timepoint;
            mAniList.add(status);
        }
 
	@Override
        public void initialize(int width, int height, int parentWidth, int parentHeight) 
	{
            super.initialize(width, height, parentWidth, parentHeight);
        }
 
	// 最重要的方法
	@Override
        protected void applyTransformation(float interpolatedTime, Transformation t) 
	{
            if (mAniList.size() < 1)
                return;
            int index1 = 0;
            int index2 = 1;
            for (int i = 0; i < mAniList.size() - 1; ++i) {
                if (interpolatedTime >= mAniList.get(i).Time && interpolatedTime <= mAniList.get(i + 1).Time)     {
                    index1 = i;
                    index2 = i + 1;
                    break;
                }
            }
	    // 在keyStatus1和keyStatus2指定的状态之间做动画
            AnimationStatus keyStatus1 = mAniList.get(index1);
            AnimationStatus keyStatus2 = mAniList.get(index2);
            if (keyStatus1 == null || keyStatus2 == null)
                return;
 
	    // 取得当前变换矩阵
	    Matrix matrix = t.getMatrix();
 
	    // 调整透明度
            float alpha1 = keyStatus1.Alpha;
            float alpha2 = keyStatus2.Alpha;
            t.setAlpha(alpha1 + (alpha2 - alpha1) * (offset_time) / timeTotal);
 
            // 调整位移
            float x1 = keyStatus1.X;
            float y1 = keyStatus1.Y;
            float x2 = keyStatus2.X;
            float y2 = keyStatus2.Y;
            matrix.preTranslate(x1 + (x2 - x1) * offset_time / timeTotal, y1 + (y2 - y1) * offset_time / timeTotal);
 
            // 调整大小
            float sx1 = keyStatus1.Scale_X;
            float sy1 = keyStatus1.Scale_Y;
            float sx2 = keyStatus2.Scale_X;
            float sy2 = keyStatus2.Scale_Y;
            matrix.preScale(sx1 + (sx2 - sx1) * offset_time / timeTotal, sy1 + (sy2 - sy1) * offset_time / timeTotal);}

以上就是一个最简单的自定义动画,我们可以连续加入任意多个AnimationStatus来指定动画的关键状态,比如(0,0,1,1,1) –> (10,50,1.5f,1.5f,0.5f)–>(10,-50,2,2,0),表示从初始状态,首先移动到(10,50)坐标处并且长宽变大1.5倍、Alpha变成0.5;然后再次移动到(10,-50)坐标处并且长宽变为2倍、Alpha变成0。 这样实现的好处在于,跟两个AnimationSet顺序执行相比,消除了AniamtionSet切换时的”卡顿”现象,整个动画流程将流畅很多。

当然,上述的实现中,在每两个关键帧之间的动画,采取的只是简单的线性函数,即按照时间(interpolatedTime)变量的一元一次方程,其实我们可以采用更复杂的计算函数(变量为interpolatedTime)来计算每一次的位移等,从而可以实现任何复杂的动画曲线。

原创文章,转载请注明:转载自ian的个人博客[http://www.icodelogic.com]
本文链接地址: http://www.icodelogic.com/?p=616

tags:

3条评论

  1. albert 说:

    請問這個有工程包可以參考一下嗎??

    • ian 说:

      不好意思这是一个大项目里面的,我不方便提供完整的工程,其实文章里提供的ExtAnimation这个类的使用也很简单的,跟其他的Animation使用方法是一样的

  2. skibug 说:

    offset_time/timeTotal 这些哪里来的呢 ,这里没定义呀

发表评论

你需要先 登录 才能回复