Android 動畫之屬性動畫(三)

Android 動畫之屬性動畫(三)#


Animation Listeners##

前面的兩節中都或多或少提到了動畫監聽 Animation Listeners 這個概念。它用來監聽動畫的過程,并且提供一些接口讓你實現一些邏輯。比如說你想在動畫開始的時候進行一些操作,就需要為這個動畫設置一個監聽器,然后實現你的邏輯,這樣,當監聽器監聽到動畫開始,就回去實現你的邏輯

Animator.AnimatorListener

Animator 類當中提供了一個 addListener() 方法,這個方法接收一個接口類 AnimatorListener ,獲得該接口后就可以重寫以下四個方法:

  • onAnimationStart() - 當動畫開始的時候調用
  • onAnimationEnd() - 當動畫結束后調用
  • onAnimationRepeat() - 當動畫重復播放時調用
  • onAnimationCancel() - 當動畫被取消的時候調用,并且在調用完這個方法后,會自動調用 onAnimationEnd() 方法

下面的例子中為每一段代碼實現了一個邏輯:彈出對應的文字

    anim.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
            Toast.makeText(MainActivity.this, "start", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            Toast.makeText(MainActivity.this, "End", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            Toast.makeText(MainActivity.this, "Cancel", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onAnimationRepeat(Animator animator) {
            Toast.makeText(MainActivity.this, "rapeat", Toast.LENGTH_LONG).show();
        }
    });
    anim.start();
    
    //添加一個按鈕,按鈕的功能用于cancel動畫
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            anim.cancel();
        }
    });

來看運行后效果



按下cancel鍵后


當然如果你不想把所有方法都重寫的話,可以使用 AnimatorListenerAdapter 類,如下

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

這樣的話就可以選擇自己想要重寫方法進行重寫

ValueAnimator.AnimatorUpdateListener

Animator 類當中提供了一個 addUpdateListener() 方法,這個方法接收一個接口類 AnimatorUpdateListener ,獲得該接口后就可以重寫 onAnimationUpdate() 方法,這個方法為動畫提供每一幀的監聽,之前在(一)的時候提到,每一幀其實是用一個 value 值來控制的,所以通常情況下我們都需要在每一幀監聽的時候獲得這個 value 值,就需要調用 ValueAnimator.getAnimatedValue() 方法來獲得。

    ValueAnimator vanim = ValueAnimator.ofInt(0,10,20);
    vanim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {

   //如果之前的ValueAnimtor指定的是Int的i話,那么返回的Value就是int類型,
   也就是返回值類型與你創建的類型一致
            int value = (int) valueAnimator.getAnimatedValue();
        }
    });

Animating Layout Changes to ViewGroups##

屬性動畫不僅可以讓你對 view 類進行動畫操作,同時它還可以用于 viewgroup 類的變化進行動畫操作。比如說你有一個 gridLayout ,你需要在里面添加按鈕,那么按鈕加到 gridLayout 是一個動畫的過程,系統會有一個默認的動畫。如果想要改變這個默認的動畫效果,就可以借助 LayoutTransition 類來為這個 gridLayout 修改動畫效果。使用步驟:

  1. 新建一個 LayoutTransition
  2. LayoutTransition 通過調用 setAnimator ( int transitionType, Animator animator ) 設置自定義的動畫
  3. 為所需要的 ViewGroup 通過 setLayoutTransition( LayoutTransition ) 添加一個 LayoutTransition

需要注意的是在setAnimator()中的第一個參數 transitionType 決定了動畫的類型,一共有四種:

  • APPEARING:元素在容器中顯現時需要動畫顯示

  • CHANGE_APPEARING:由于容器中要顯現一個新的元素,其它元素的變化需要動畫顯示

  • DISAPPEARING:元素在容器中消失時需要動畫顯示

  • CHANGE_DISAPPEARING:由于容器中某個元素要消失,其它元素的變化需要動畫顯示

      代碼:
      LayoutTransition layoutTransition = new LayoutTransition();
      //設置這個動畫為縮放
      ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(null,"scaleX",0f,1f);
      layoutTransition.setAnimator(LayoutTransition.APPEARING,objectAnimator);
      gridContainer.setLayoutTransition(layoutTransition);
    
      //通過點擊添加按鈕,往gridContainer添加button,這里就涉及到了上面第一種類型的動畫:appear
    
      add_button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Button newButton = new Button(MainActivity.this);
              newButton.setText(String.valueOf(num++));
              gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount()));
          }
      });
    

可以看到,按鈕出現的時候是以縮放的形式出現的



更多關于 LayoutTransition 請查看文檔
https://developer.android.com/reference/android/animation/LayoutTransition.html

Using a TypeEvaluator

在第一篇的時候我們就有講到 Evaluator 這個類。請復習 Android 動畫之屬性動畫(一)>Evaluator 類。有些時候你需要返回的動畫返回的 value 不是系統默認的 Int,Float 或 Color 類型的,那么你就可以自己自定義這個 Evaluator 來返回你所需要的類型!比如說一個坐標點。這也是 屬性動畫補間動畫 強大的精髓所在。

那要如何自己實現呢,首先需要繼承 TypeEvaluator 接口,然后重寫里面的 evaluate() 方法。

//其實FloatEvaluator也是繼承TypeEvaluator,來實現的
public class FloatEvaluator implements TypeEvaluator {
//注意看到fraction參數,在第一篇里面提到的參數
public Object evaluate(float fraction, Object startValue, Object endValue) {
    float startFloat = ((Number) startValue).floatValue();
    return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}

下面我們來實現一個返回一個坐標點的 Evaluator

首先新建一個點類:Point

public class Point {
private float x;
private float y;
public Point(float x,float y) {
this.x = x;
this.y = y;
}

public float getX(){
    return x;
}
public  float getY(){
    return y;
}
}

然后我們新建一個 PointEvaluator ,在里面要實現的是當我們傳入一個初始點和結束點,然后實現平緩從初始點到結束點

public class PointEvaluator implements TypeEvaluator {
float x;
float y;
@Override
public Object evaluate(float fraction, Object s, Object e) {
    Point startPoint = (Point) s;
    Point endPoint = (Point) e;
    float x = startPoint.getX()+fraction*(endPoint.getX()-startPoint.getX());
    float y = startPoint.getY()+fraction*(endPoint.getY()-startPoint.getY());
    Point point = new Point(x,y);
    return point;
}
}

然后我們新建一個view,在view里面有一個繪制一個圓,通過對圓心坐標的動畫實現球的動畫
代碼的思路就是在左上角畫一個圓,然后對圓心坐標進行進行動畫,為動畫監聽,每得到一個新的圓心坐標就重新繪制view,下面代碼略長,耐心看是很容易看懂的

public class myView extends View {                                                                       
Paint mpaint;                                                                                        
Point CurrentPoint = null;                                                                           
float radius;                                                                                        
                                                                                                     
public myView(Context context) {                                                                     
    this(context, null);                                                                             
}                                                                                                    
                                                                                                     
public myView(Context context, AttributeSet attrs) {                                                 
    super(context, attrs);                                                                           
    mpaint = new Paint();                                                                            
    mpaint.setStyle(Paint.Style.FILL);                                                               
    mpaint.setColor(Color.BLUE);                                                                     
    radius = 25f;                                                                                    
} 

//CurrentPoint用來記錄當前圓心的位置,用來告訴canvas在哪繪制圓,Currentpoint為空時
則新建一個坐標點,然后設置動畫                                                                                                   
                                                                                                     
@Override                                                                                            
protected void onDraw(Canvas canvas) {                                                               
    super.onDraw(canvas);                                                                            
    if (CurrentPoint == null) {                                                                      
        CurrentPoint = new Point(radius, radius);                                                    
        drawCircle(canvas);                                                                          
        setAnimation();                                                                              
    } else {                                                                                         
        drawCircle(canvas);                                                                          
    }                                                                                                
}                                                                                                    
 
//根據當前坐標畫圓                                                                                                    
private void drawCircle(Canvas canvas) {                                                             
    canvas.drawCircle(CurrentPoint.getX(), CurrentPoint.getY(), radius, mpaint);                     
}                                                                                                    

//設置動畫。每當有新的point返,則通知view重繪                                                                                                     
public void setAnimation() {                                                                         
      ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),                     
              new Point(radius,radius),
              new Point(getWidth() - radius, getHeight() - radius));        
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {                     
        @Override                                                                                    
        public void onAnimationUpdate(ValueAnimator valueAnimator) {                                 
            CurrentPoint = (Point) valueAnimator.getAnimatedValue();                                 
            invalidate();                                                                            
        }                                                                                            
    });                                                                                              
    valueAnimator.setDuration(2000);                                                                 
    valueAnimator.start();                                                                           
}                                                                                                    

}

到此我們已經完成了自定義的 Evaluator ,請自行運行查看效果

Using Interpolators##

前面已經提到過 Interpolators 的作用,這里不再重復。如果你沒有找到適合自己的 Interpolators ,那么就可以繼承 Interpolators 然后重寫 getInterpolation()
方法

以下是系統實現 AccelerateDecelerateInterpolatorLinearInterpolator 的算法:

AccelerateDecelerateInterpolator

public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

public float getInterpolation(float input) {
    return input;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1 背景 不能只分析源碼呀,分析的同時也要整理歸納基礎知識,剛好有人微博私信讓全面說說Android的動畫,所以今...
    未聞椛洺閱讀 2,758評論 0 10
  • 寫的非常好,強烈推薦給大家 轉載請注明出處:http://blog.csdn.net/guolin_blog/ar...
    天天大保建閱讀 810評論 0 1
  • 在手機上去實現一些動畫效果算是件比較炫酷的事情,因此Android系統在一開始的時候就給我們提供了兩種實現動畫效果...
    Ten_Minutes閱讀 3,882評論 3 11
  • 最近在做個項目,用到了藍牙打印。一切都是從零開始。踩了很多坑,搞了好幾天才搞好,現分享出來,讓別人少走彎路!我用的...
    黑森林中的小木屋閱讀 1,199評論 7 0
  • 今天是本書的最后一個部分,主要內容是,任何經歷都是有價值的,只要你愿意提供你的價值,機會就會出現。而你不需要成為一...
    步履如菲閱讀 747評論 0 52