自定義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這個作者寫的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章