Android 動(dòng)畫之屬性動(dòng)畫(三)

Android 動(dòng)畫之屬性動(dòng)畫(三)#


Animation Listeners##

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

Animator.AnimatorListener

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

  • onAnimationStart() - 當(dāng)動(dòng)畫開始的時(shí)候調(diào)用
  • onAnimationEnd() - 當(dāng)動(dòng)畫結(jié)束后調(diào)用
  • onAnimationRepeat() - 當(dāng)動(dòng)畫重復(fù)播放時(shí)調(diào)用
  • onAnimationCancel() - 當(dāng)動(dòng)畫被取消的時(shí)候調(diào)用,并且在調(diào)用完這個(gè)方法后,會(huì)自動(dòng)調(diào)用 onAnimationEnd() 方法

下面的例子中為每一段代碼實(shí)現(xiàn)了一個(gè)邏輯:彈出對(duì)應(yīng)的文字

    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();
    
    //添加一個(gè)按鈕,按鈕的功能用于cancel動(dòng)畫
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            anim.cancel();
        }
    });

來看運(yùn)行后效果



按下cancel鍵后


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

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

這樣的話就可以選擇自己想要重寫方法進(jìn)行重寫

ValueAnimator.AnimatorUpdateListener

Animator 類當(dāng)中提供了一個(gè) addUpdateListener() 方法,這個(gè)方法接收一個(gè)接口類 AnimatorUpdateListener ,獲得該接口后就可以重寫 onAnimationUpdate() 方法,這個(gè)方法為動(dòng)畫提供每一幀的監(jiān)聽,之前在(一)的時(shí)候提到,每一幀其實(shí)是用一個(gè) value 值來控制的,所以通常情況下我們都需要在每一幀監(jiān)聽的時(shí)候獲得這個(gè) value 值,就需要調(diào)用 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類型,
   也就是返回值類型與你創(chuàng)建的類型一致
            int value = (int) valueAnimator.getAnimatedValue();
        }
    });

Animating Layout Changes to ViewGroups##

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

  1. 新建一個(gè) LayoutTransition
  2. 對(duì) LayoutTransition 通過調(diào)用 setAnimator ( int transitionType, Animator animator ) 設(shè)置自定義的動(dòng)畫
  3. 為所需要的 ViewGroup 通過 setLayoutTransition( LayoutTransition ) 添加一個(gè) LayoutTransition

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

  • APPEARING:元素在容器中顯現(xiàn)時(shí)需要?jiǎng)赢嬶@示

  • CHANGE_APPEARING:由于容器中要顯現(xiàn)一個(gè)新的元素,其它元素的變化需要?jiǎng)赢嬶@示

  • DISAPPEARING:元素在容器中消失時(shí)需要?jiǎng)赢嬶@示

  • CHANGE_DISAPPEARING:由于容器中某個(gè)元素要消失,其它元素的變化需要?jiǎng)赢嬶@示

      代碼:
      LayoutTransition layoutTransition = new LayoutTransition();
      //設(shè)置這個(gè)動(dòng)畫為縮放
      ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(null,"scaleX",0f,1f);
      layoutTransition.setAnimator(LayoutTransition.APPEARING,objectAnimator);
      gridContainer.setLayoutTransition(layoutTransition);
    
      //通過點(diǎn)擊添加按鈕,往gridContainer添加button,這里就涉及到了上面第一種類型的動(dòng)畫: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()));
          }
      });
    

可以看到,按鈕出現(xiàn)的時(shí)候是以縮放的形式出現(xiàn)的



更多關(guān)于 LayoutTransition 請(qǐng)查看文檔
https://developer.android.com/reference/android/animation/LayoutTransition.html

Using a TypeEvaluator

在第一篇的時(shí)候我們就有講到 Evaluator 這個(gè)類。請(qǐng)復(fù)習(xí) Android 動(dòng)畫之屬性動(dòng)畫(一)>Evaluator 類。有些時(shí)候你需要返回的動(dòng)畫返回的 value 不是系統(tǒng)默認(rèn)的 Int,F(xiàn)loat 或 Color 類型的,那么你就可以自己自定義這個(gè) Evaluator 來返回你所需要的類型!比如說一個(gè)坐標(biāo)點(diǎn)。這也是 屬性動(dòng)畫補(bǔ)間動(dòng)畫 強(qiáng)大的精髓所在。

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

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

下面我們來實(shí)現(xiàn)一個(gè)返回一個(gè)坐標(biāo)點(diǎn)的 Evaluator

首先新建一個(gè)點(diǎn)類: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;
}
}

然后我們新建一個(gè) PointEvaluator ,在里面要實(shí)現(xiàn)的是當(dāng)我們傳入一個(gè)初始點(diǎn)和結(jié)束點(diǎn),然后實(shí)現(xiàn)平緩從初始點(diǎn)到結(jié)束點(diǎn)

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;
}
}

然后我們新建一個(gè)view,在view里面有一個(gè)繪制一個(gè)圓,通過對(duì)圓心坐標(biāo)的動(dòng)畫實(shí)現(xiàn)球的動(dòng)畫
代碼的思路就是在左上角畫一個(gè)圓,然后對(duì)圓心坐標(biāo)進(jìn)行進(jìn)行動(dòng)畫,為動(dòng)畫監(jiān)聽,每得到一個(gè)新的圓心坐標(biāo)就重新繪制view,下面代碼略長(zhǎng),耐心看是很容易看懂的

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用來記錄當(dāng)前圓心的位置,用來告訴canvas在哪繪制圓,Currentpoint為空時(shí)
則新建一個(gè)坐標(biāo)點(diǎn),然后設(shè)置動(dòng)畫                                                                                                   
                                                                                                     
@Override                                                                                            
protected void onDraw(Canvas canvas) {                                                               
    super.onDraw(canvas);                                                                            
    if (CurrentPoint == null) {                                                                      
        CurrentPoint = new Point(radius, radius);                                                    
        drawCircle(canvas);                                                                          
        setAnimation();                                                                              
    } else {                                                                                         
        drawCircle(canvas);                                                                          
    }                                                                                                
}                                                                                                    
 
//根據(jù)當(dāng)前坐標(biāo)畫圓                                                                                                    
private void drawCircle(Canvas canvas) {                                                             
    canvas.drawCircle(CurrentPoint.getX(), CurrentPoint.getY(), radius, mpaint);                     
}                                                                                                    

//設(shè)置動(dòng)畫。每當(dāng)有新的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();                                                                           
}                                                                                                    

}

到此我們已經(jīng)完成了自定義的 Evaluator ,請(qǐng)自行運(yùn)行查看效果

Using Interpolators##

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

以下是系統(tǒng)實(shí)現(xiàn) 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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容