自定义view(四) 动画的使用及自定义 ,仿qq抖动

在前面的文章中我们写了view中的图形的绘制以及水波纹的效果,从中我们可以看出做一些稍微复杂的效果都需要用到动画的功能,在android系统api1中提供了视图动画,在api11的时候提供了属性动画,接下来我们就开始讲讲基本的动画功能。

  • 视图动画使用

通过官网我们可以看到视图动画,可以简单分为两类,补间动画和帧动画,补间动画包括位移,旋转,透明度变化,缩放变化。 帧动画就是提供一些列的drawable连续播放。

                作用                            类名
 对view进行位移TranslateAnimation
 对view进行旋转RotateAnimation
 对view进行缩放ScaleAnimation
 设置view的透明度AlphAnimation
 drawable 连续播放DrawableAnimation

在使用动画之前首先要明确的是,补间动画只能是view与它的子类可以使用。通过view.startAnimation(Animation) 来实现。所有的动画都有两种方式进行实现。一个是在代码中实现,一个是在xml中创建。 通过AnimationUtils.loadAnimation()来获取实例。

 ImageView iv = findViewById(R.id.rotate_img);
        iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                beginAnimation();
                float left = iv.getLeft();
                float right = iv.getRight();
                int top = iv.getTop();
                int bottom = iv.getBottom();
                
                //围绕图片中心点进行旋转,旋转180度
                animation = new RotateAnimation(0,180,(right-left)/2,(bottom-top)/2);
                //设置旋转所需时间
                animation.setDuration(2000);
                //fillafter属性设置为true,表示动画执行完毕保存执行之后的状态,不会复位
                animation.setFillAfter(true);

                //为这个动画设置一个时间插入器,决定动画执行的快慢方式。
                animation.setInterpolator(new AccelerateDecelerateInterpolator());
                //开始动画
                iv.startAnimation(animation);
            }
        });

这就是一个简单的通过代码来设置旋转动画,又或者我们可以通过创建xml的方式来实现动画,动画的资源文件放在/res/anim目录下, 比如我们做一个平移动画translate.xml:

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

    <translate
        android:fromXDelta="100"
        android:fromYDelta="0"
        android:toXDelta="400"
        android:toYDelta="600"
        />
</set>

 然在在代码中来实现:

final ImageView iv = findViewById(R.id.scale_img);
        iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               
                iv.startAnimation(animation);
            }
        });
        animation = AnimationUtils.loadAnimation(this,R.anim.translate);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                
            }

            @Override
            public void onAnimationEnd(Animation animation) {

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        animation.setDuration(2000);
        animation.setFillAfter(true);

这就是两种动画实现方式的简单展示。在使用动画的时候我们可以通过设置监听的方式对动画的开始,结束,以及重复进行监听,在回调函数里做出相应的动作。

在大多数的情况下,一个单一的动画无法实现我们的需求。 当需要多个动画组合执行的时候,可以用AnimationSet来讲所有动画集合起来执行。 其实AnimationSet就可以看做是一个动画的集合。 下面就是使用set的一个简单例子。 当时set也可以通过xml文件实现。

private void beginAnimation() {
        // 创建动画集合
        AnimationSet aniSet = new AnimationSet(false);

        float left = iv.getLeft();
        float right = iv.getRight();
        int top = iv.getTop();
        int bottom = iv.getBottom();
        Log.e("tag","the left="+left+", right="+right+", top="+top+", bottom="+bottom);
        // 透明度动画
        AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
        alpha.setDuration(time);
        aniSet.addAnimation(alpha);



        // 旋转动画
        RotateAnimation rotate = new RotateAnimation(0,720,(right-left)/2,(bottom-top)/2);
        rotate.setDuration(time);
        aniSet.addAnimation(rotate);

        // 缩放动画
        ScaleAnimation scale = new ScaleAnimation(0.1f, 1, 0.1f, 1f,(right-left)/2,(bottom-top)/2);
        scale.setDuration(time);
        aniSet.addAnimation(scale);


        TranslateAnimation translate = new TranslateAnimation(right, left, bottom, top);
        translate.setDuration(time);
        aniSet.addAnimation(translate);

        // 动画监听
        aniSet.setAnimationListener(new Animation.AnimationListener() {
            // 动画开始
            @Override
            public void onAnimationStart(Animation animation) {

            }

            // 动画结束,一般在这里实现页面跳转逻辑
            @Override
            public void onAnimationEnd(Animation animation) {
                // 动画结束后,跳转到主页面
           //     startActivity(new Intent(RotateActivity.this, MainActivity.class));
            }

            // 动画重复
            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        // 把动画设置给llGroup
        iv.startAnimation(aniSet);
    }

这几个可视动画都是继承Animation,其实动画可以解析成几个模块,首先做什么样的动作(平移,旋转等)。 第二时间(需要多久执行完),第三时间插入器(Interpolator )它决定动画执行的速率。加速执行还是减速或者匀速执行。最后可以添加动画监听。而其实我们所做的各个动画,其实本质上可以看做是矩阵的变换执行结果。想对view动画进行重写必须对矩阵Matrix清楚。这里有一篇矩阵的博客写的很好, 希望大家看看这篇博客,会对矩阵有更清晰的认识。

  • qq抖动

下面我们就通过继承Animation来实现自己所需动画的功能。首先我们分析Animation.java的源码发现子类必须继承实现applyTransformation()这个方法

 /**
     * Helper for getTransformation. Subclasses should implement this to apply
     * their transforms given an interpolation value.  Implementations of this
     * method should always replace the specified Transformation or document
     * they are doing otherwise.
     *
     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
     *        after it has been run through the interpolation function.
     * @param t The Transformation object to fill in with the current
     *        transforms.
     */
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    }

通过注释说明可以看出interpolatedTime是一个时间因子,他是一个范围从0到1的float类型,它表示动画已经执行的时间占总时间的百分比。transformation表示的相当于在某个时间点的动画的转换。我们可以通过它获取当前变换的矩阵,然后做自己的所需要的操作。

public class MyTranslateAnimation extends Animation{

    float  x,y,tox,toy;


    //这个类的本意是模拟实现translateAnimation,当用作抖动动画使用的时候,参数无意义
    public MyTranslateAnimation(float fromx,float tox,float fromy,float toy) {
        this.x = fromx;
        this.y = fromy;
        this.tox = tox;
        this.toy = toy;
    }


    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t){
        //模拟的translateAnimation实现

       /* float dx = x + (tox -x)*interpolatedTime;
        float dy = y + (toy - y)*interpolatedTime;
        t.getMatrix().setTranslate(dx, dy);*/

        //因为interpolatedTime为0到1,所以乘以10π,表示5个sin周期,这个值越大,晃动的频率就越 
        //高,
        //因为Math.sin(interpolatedTime*10*Math.PI)他的值为[-1,1]所以以向左向右10像素移动。
        t.getMatrix().setTranslate((float)(Math.sin(interpolatedTime*10*Math.PI)*10),
                (float)Math.sin(interpolatedTime*10*Math.PI)*5);
        super.applyTransformation(interpolatedTime,t);
    }
}

直接通过以下代码就可以实现都图片的抖动:

public class TranslateActivity extends AppCompatActivity {

    MyTranslateAnimation animation;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_translate);

        final ImageView iv = findViewById(R.id.translate_img);
        iv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                iv.startAnimation(animation);

                
            }
        });
        animation = new MyTranslateAnimation(0,1000,0,500);
        animation.setFillAfter(true);
        animation.setDuration(2000);
    }

其实无论是想实现什么样的动画,都是需要通过时间因子与matrix相互结合然后实现的。就像上面模拟位移动画一样,我们可以看看translateAnimation的源码:

  @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float dx = mFromXDelta;
        float dy = mFromYDelta;
        if (mFromXDelta != mToXDelta) {
            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
        }
        if (mFromYDelta != mToYDelta) {
            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
        }
        t.getMatrix().setTranslate(dx, dy);
    }

原理上与我们自己写的是一致的, 如果有空可以看看其他几个动画的源码。你就发现其实并不难。

  • 总结

实现自己需要的动画,一定要了解矩阵的知识,推荐:https://www.cnblogs.com/fordreamxin/p/4721497.html这个作者写的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值