祖傳Android屬性動畫詳解

動畫,顧名思義,一個很神奇的東西,第一次在android中接觸這個東西,run以後感覺挺好玩的,但是,長江前浪推後浪,後浪把前浪拍在了沙灘上。Android爲我們提供了幾種動畫類型:View Animation 、Drawable Animation 、Property Animation 。View Animation相當簡單,不過只能支持簡單的縮放、平移、旋轉、透明度基本的動畫,且有一定的侷限性。比如:你希望View有一個顏色的切換動畫;你希望可以使用3D旋轉動畫;你希望當動畫停止時,View的位置就是當前的位置;這些View Animation都無法做到。這就是Property Animation產生的原因,今天我將和大家一起完成屬性動畫(Property Animation)的學習。
Property Animation,是一個google3.0以後才提出的動畫框架,那麼有人就會問了,3.0之前的我們就不管了嗎,NO,NO~~,有的大神早已經爲我們考慮到了這一點,導入Nineoldandroids點擊下載動畫庫,Nineoldandroids包,由Github大牛jake wharton 開發,令Property Animation可以支持到1.x,用法基本完全一致~
導入
1. 屬性動畫Animator傳統動畫Animation
2. Animation侷限性
3. 相關API
Animation動畫簡單的來說就是系統不斷的調用OnDraw()重繪界面來實現一個動畫效果,而屬性動畫,顧名思義,就是去操縱一個屬性(getXX,setXX方法)從而真實改變一個屬性實現動畫效果。
下面通過一個簡單的代碼段子咱們來看看這兩個傢伙到底有什麼區別。
佈局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:onClick="imageClick"
        android:id="@+id/imageView" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="看上面的圖片"
        android:id="@+id/btnMove"
        android:onClick="buttonMove"
        android:layout_gravity="center_horizontal"
        android:layout_below="@+id/imageView"
        android:layout_centerHorizontal="true" />
 </RelativeLayout>

很簡單的佈局,就是一個圖片,一個按鈕
Activity代碼

public class MainActivity extends ActionBarActivity implements View.OnClickListener {

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //隱藏標題欄
        getSupportActionBar().hide();
        setContentView(R.layout.activity_main);
    }

    //圖片的點擊事件
    public void imageClick(View v) {
        Toast.makeText(this, "圖片在這裏----->OnClick", Toast.LENGTH_SHORT).show();
        //TODO
    }
    //按鈕的點擊事件,ImageView移動
    public void buttonMove(View v) {
        //平移動畫
        TranslateAnimation ta = new TranslateAnimation(0,500,0,0);
        ta.setDuration(1000);
        ta.setFillAfter(true);
        ImageView iv = (ImageView) findViewById(R.id.imageView);
        iv.startAnimation(ta);
        }
  }

界面

這裏寫圖片描述
點擊按鈕移動,然後點擊圖片卻沒有toast,但是點擊圖片的原始位置卻toast了,這就是視圖動畫最大的一個缺陷就是隻是重繪動畫,改變了他的顯示位置,但是真真正正響應的事件卻沒有發生變化,所以這種視圖動畫不適合做用戶交互的動畫,他只是可以僅僅實現一些顯示給用戶的效果。從cpu資源來看,傳統的視圖動畫只是不斷的調用onDraw()方法,所以對於系統來說是很耗費資源的。雖然說可以通過各種組合也可以實現各種各樣的動畫效果,但是和一個屬性來說是遠遠不夠的。google給我們提供了一個祕密武器,可以讓我們輕輕鬆鬆的實現更多,更優秀的動畫效果。
下面開始進入我們的三境界:
第一重境界-ObjectAnimator

  1. 實現Animation框架的功能
  2. 屬性動畫常用屬性演示
  3. 動畫的監聽事件
    將前面的代碼稍微修改一下,就可以實現了
 //按鈕的點擊事件,ImageView移動
    public void buttonMove(View v) {
        ImageView iv = (ImageView) findViewById(R.id.imageView);
        //使用屬性動畫實現平移效果,節省系統資源
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv, "translationX", 0F, 400F);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
        }

ofFloat參數說明(我們將要操縱的對象,我們將要操縱的屬性,一個可變長度的數組)
運行效果也很明顯,圖片的點擊事件始終隨着圖片的移動而移動。
屬性動畫更新的過程中,會不斷調用setPropName更新元素的屬性,所有使用ObjectAnimator更新某個屬性,必須得有getter(設置一個屬性值的時候)和setter方法~
我們還可以對imageview做什麼樣的動畫哪?只要google給我們提供了get,set方法我們就可以實現,常用的屬性會在後面給大家一一列出。
這只是單屬性動畫效果,那麼多屬性動畫複雜效果是怎麼實現的哪?上代碼

//使用屬性動畫實現複雜動畫的
 public void buttonMove(View v) {
 ImageView iv = (ImageView) findViewById(R.id.imageView);
        PropertyValuesHolder pv = PropertyValuesHolder.ofFloat("rotation",0,360F);
        PropertyValuesHolder pv1 = PropertyValuesHolder.ofFloat("translationX",0,200F);
        PropertyValuesHolder pv2 = PropertyValuesHolder.ofFloat("translationY",0,200F);
ObjectAnimator.ofPropertyValuesHolder(iv,pv,pv1,pv2).setDuration(2000).start();
}

效果很明顯,三個動畫一起執行,這裏就不貼圖了
這裏不知道你有沒有想到,之前我們學習Animation動畫的時候,學個一個動畫集合AnimationSet,那麼這個屬性動畫有沒有哪?同樣也有,他是AnimatorSet,好了不多說,直接上代碼

public void buttonMove(View v) {
 ImageView iv = (ImageView) findViewById(R.id.imageView);
 ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(iv,"rotation",0F,200F);
        ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(iv,"translationX",0,200F);
        ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(iv,"translationY",0,200F);
        AnimatorSet set = new AnimatorSet();
set.playTogether(objectAnimator1,objectAnimator2,objectAnimator3);
setDuration(2000);
        set.start();
}

效果和上述的一樣。
代碼中有這麼一句,

set.playTogether(objectAnimator1,objectAnimator2,objectAnimator3);

三個動畫同時進行,但是有些時候需求並不是這樣的,我們想要的動畫效果,那要怎麼實現那,我們來看一下里面還有什麼方法。

set.playSequentially(objectAnimator1,objectAnimator2,objectAnimator3);

這一句,顧名思義,動畫按照先後順序來進行,三個動畫依次進行,一個完了接着下一個,動畫先在原地旋轉360°,然後再X軸上移動200的距離,最後在Y軸上移動200的距離。。
還有一種這樣的,就是上述的動畫我們改一下,我們先讓動畫在X軸上,Y軸上同時的平移,最後再旋轉360°,怎麼做哪?

set.play(objectAnimator2).with(objectAnimator3);
set.play(objectAnimator1).before(objectAnimator2);

set裏面還有一個方法before方法,大家一看便知,這裏就讓你們自己去試試吧。
通過動畫集合框架中的play,playSequentially,playTogether,with,before,after,等方法就可以輕鬆的實現各種動畫。
我們已經見識到了各種動畫的強大了,這只是給用戶的一個展示效果,如果他沒有作用,我們寫了也沒有什麼用啊,那麼我們就要給他加各種動畫的監聽事件,讓動畫得知在什麼時候我們該做什麼了,下面我們就來看看動畫的監聽事件。
佈局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:onClick="click"
    android:text="click"/>

</LinearLayout>

佈局文件很簡單,就只有一個button,一個點擊事件
activity文件


/**
 * Created by THINK on 2015/4/26 23:50.
 */
public class aaa extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aaa);

    }

    public void click(View v) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(v,"alpha",0F,1F);
        objectAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Toast.makeText(aaa.this,"哎呀,動畫結束了",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        objectAnimator.setDuration(2000);
        objectAnimator.start();
    }
}

就是一個button的漸顯動畫,然後添加了動畫監聽事件,實現四個方法,很簡單,動畫開始,動畫結束,動畫取消,重新加載動畫時分別調用這四個方法。爲了簡單看見效果,我在動畫結束的時候Toast了一句話。
但是有些人說,這樣子看着不順眼,實現一個動畫監聽事件還要加四個方法,我們通常只會用到動畫結束時的方法,這個樣子我們還是有辦法的,適配器模式就可以很輕鬆的實現了。

 objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });

將監聽器換成此樣子就可以了,而且這裏你想要什麼樣的方法就可以添加如此。
再這裏帶大家完成一個好玩的例子,算是給第一重境界做個總結吧。大家不知道見過Path這個App的一個button彈出效果嗎
這裏寫圖片描述
如果大家有研究過他的源碼,會發現好囉嗦,好麻煩啊,我們剛剛學了屬性動畫,下面我們就用屬性動畫來去搞他了。
佈局文件xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewb"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_b" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewc"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_c" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewd"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_d" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewe"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_e" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewf"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_f" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewg"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_g" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageViewh"
        android:layout_margin="15dp"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_h" />

    <ImageView
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:id="@+id/imageViewa"
        android:layout_gravity="left|top"
        android:src="@mipmap/composer_a" />
</FrameLayout>

通過使用幀佈局,將8個圖片依次疊加放置在佈局中,然後我們點擊上面的圖片,會依次實現上面圖片的效果,
activity類


/**
 1. Created by THINK on 2015/4/27 0:10.
 */
public class AnimActivity extends ActionBarActivity implements View.OnClickListener {
    private int[] imageRes = {R.id.imageViewa, R.id.imageViewb,
            R.id.imageViewc, R.id.imageViewd, R.id.imageViewe,
            R.id.imageViewf, R.id.imageViewg, R.id.imageViewh};
    private List<ImageView> imageViewList = new ArrayList<ImageView>();
    private boolean falg = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();
        setContentView(R.layout.anim_layout);
        initImage();
    }

    private void initImage() {
        for (int i = 0; i < imageRes.length; i++) {
            ImageView imageView = (ImageView) findViewById(imageRes[i]);
            imageView.setOnClickListener(this);
            imageViewList.add(imageView);
        }
    }

    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.imageViewa:

                if (falg){
                    //點擊開始動畫
                    startAnim();
                }else{
                    //點擊關閉動畫
                    closeAnim();
                }
                break;
            default:
                //點擊其他,這就是屬性的動畫的好處,每個控件的點擊都會隨着動畫的移動而移動。case情況就是按鈕的幾種效果,這裏就不細寫了
                Toast.makeText(this, v.getId() + "", Toast.LENGTH_LONG).show();
                break;
        }
    }

    private void closeAnim() {
        for (int i = 1; i < imageRes.length; i++) {
            ObjectAnimator animator = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", i * 180F, 0F);
            animator.setDuration(300);
            animator.setStartDelay(i * 200);
            animator.setInterpolator(new BounceInterpolator());
            animator.start();
        }
        falg = true;
    }


    private void startAnim() {
        for (int i = 1; i < imageRes.length; i++) {
            ObjectAnimator animator = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0F, i * 150F);
            animator.setDuration(300);
            //動畫彈出的效果時間間隔
            animator.setStartDelay(i*200);
            //動畫的差值器,實現自由落體效果
            animator.setInterpolator(new BounceInterpolator());
            animator.start();
        }
        falg = false;
    }
}

學過上面的東西,再回來看這些東西就so easy了吧~!,裏面的效果還可以豐富,你們先運行一邊看看效果去吧~
這裏寫圖片描述
收放效果都有種自由落體的感覺。
第二重境界-ValueAnimator

  1. ValueAnimator是什麼
  2. ValueAnimator怎麼用
  3. ValueAnimator實例
    ValueAnimator本身不會作用與任何一個屬性,也不會提供任何一個動畫,簡單的來說她就是一個數值發生器,它可以產生你想要的各種數值,類型就是我們之前學的那些,int,float,等等,還可以自定義數值類型。
    當我們繼續深究其源碼的時候,其實ObjectAnimator就是繼承與ValueAnimator的。
    下面我們寫一個計時器的動畫效果。
    佈局文件
 <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="點我看看"
        android:id="@+id/btnchange"
        android:onClick="change"
        android:layout_below="@+id/btnMove"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="65dp" />

Activity文件

public class MainActivity extends ActionBarActivity implements View.OnClickListener {

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();
        setContentView(R.layout.activity_main);
    }
    //計時器
    public void change(View v) {
        final Button btn = (Button) findViewById(R.id.btnchange);
        ValueAnimator animator = ValueAnimator.ofInt(0, 100);
        animator.setDuration(5000);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer integer = (Integer) animation.getAnimatedValue();
                btn.setText("" + integer);
            }
        });
        animator.start();
    }

當我們點擊Button的時候,Button上的數字由0到100的一個計時器動畫效果,用時5s,數值由0到100,是不是比我們要開線程依次循壞來的簡單,來的爽吧。
其中還有這麼幾個方法
這裏寫圖片描述
如果我們想要自定義數值類型,就要用到ofObject()這個方法,下面我們來看看。

   ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
            @Override
            public Object evaluate(float fraction, Object startValue, Object endValue) {
                return null;
            }
        });

可以通過evaluate方法中三個參數的計算實現各種各樣的效果,第一個參數時間差值器,範圍0~1,第二個和第三個是開始值和結束值。
好了,屬性動畫到此告一段落,後續可能還會出,下面我們來總結一下:
常用屬性
translationX/translationY:移動距離
rotation,rotationX,rotationY:旋轉動畫距離
scaleX,scaleY:縮放動畫
X/Y:座標移動
alpha:透明動畫
雖然動畫屬性都一樣,但是實現的效果和效率卻是大大不一樣的。
下面我們來看看常用的方法,類:

  1. ValueAnimator:數值發生器
  2. ObjectAnimator:通過此操作動畫屬性,從而實現一個動畫效果
  3. AnimatorUpdateListener:動畫監聽器
  4. AnimatorListenerAdapter:動畫監聽器
  5. PropertyValuesHolder:多屬性動畫
  6. AnimatorSet:集合動畫
  7. TypeEvaluators:值計算器
  8. Interpolators:差值器
    Interpolators圖示
    這裏寫圖片描述
    希望大家可以自己繼續探索屬性動畫的奧祕,我們一起學習!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章