Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android動畫三部曲之一 View Animation & LayoutAnimation

Android動畫三部曲之一 View Animation & LayoutAnimation

編輯:關於Android編程

Tween動畫

Tween動畫又稱補間動畫。通過對view的位置、大小、透明度、角度的改變來實現動畫效果。

補間動畫的基類是Animation。我們通常使用它的直接子類RotateAnimationTranslateAnimationScaleAnimationAlphaAnimation

補間動畫可以通過xml進行定義(res/anim/xxx),然後通過AnimationUtils類進行加載;也可以通過完全代碼進行設置。


XML語法介紹

<code class="hljs xml"><!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%2D%2D%3E-->
<set android:interpolator="@[package:]anim/interpolator_resource" android:shareinterpolator="[" xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha android:fromalpha="float" android:toalpha="float">
    <scale android:fromxscale="float" android:fromyscale="float" android:pivotx="float" android:pivoty="float" android:toxscale="float" android:toyscale="float">
    <translate android:fromxdelta="float" android:fromydelta="float" android:toxdelta="float" android:toydelta="float">
    <rotate android:fromdegrees="float" android:pivotx="float" android:pivoty="float" android:todegrees="float">
    <set>
        ...
    </set>
</rotate></translate></scale></alpha></set></code>

圖片名稱

Animation類定義了很多常量和變量的初始值,比如:

public static final int INFINITE = -1;

public static final int RESTART = 1;

public static final int REVERSE = 2;

主要用到它的子類以及AnimationListener :

public static interface AnimationListener {
        /**
         * 動畫開始的時候回調
         *
         * @param animation The started animation.
         */
        void onAnimationStart(Animation animation);
        /**
         * 動畫結束的時候回調。但是當設置動畫重復次數為INFINITE的時候,該方法不會回調。
         *
         * @param animation The animation which reached its end.
         */
        void onAnimationEnd(Animation animation);
        /**
         * 動畫重復播放的時候回調
         *
         * @param animation The animation which was repeated.
         */
        void onAnimationRepeat(Animation animation);
    }

添加此監聽器可以對動畫做更多的操作。


插值器 – Interpolator

介紹動畫之前,得先說說”插值器”。插值器的意思就是在播放動畫的時候,改變播放的速率,可以使動畫越來越快,或者越來越慢等。

常用的是一下九個插值器:

Baseinterpolator子類 Resource ID 描述 AccelerateInterpolator @android:anim/accelerate_interpolator 加速變化(開始慢,越來越快) DecelerateInterpolator @android:anim/decelerate_interpolator 減速變化(開始快,越來越慢) AccelerateDecelerateInterpolator @android:anim/accelerate_decelerate_interpolator 先加速後減速(中間速度最快) LinearInterpolator @android:anim/linear_interpolator 線性均勻變化 OvershootInterpolator @android:anim/overshoot_interpolator 超出結尾的臨界值,然後在緩慢回到結束值 AnticipateInterpolator @android:anim/anticipate_interpolator 先向相反的方向改變一點,然後在加速播放 AnticipateOvershootInterpolator @android:anim/anitcipate_overshoot_interpolator 先向相反的方向改變一點,然後在加速播放至超出結束值一點,然後在緩慢回到結束值 BounceInterpolator @android:anim/bounce_interpolator 動畫快結束的時候,模擬球落地的回彈效果 CycleInterpolator @android:anim/cycle_interpolator 動畫循環播放指定的次數

自定義Interpolator

一般來說,官方API給的這幾個插值器就夠實用了。不過還可以自定義Interpolator。可以簡單的對系統的插值器進行一些參數值的修改:


公共XML屬性及對應的方法

屬性名稱 對應的方法 描述 android:duration setDuration(long) 動畫持續的時間長度(單位是miliseconds) android:interpolator setInterpolator(Interpolator) 設置動畫播放時的插值器 android:repeatCount setRepeatCount(int) 設置動畫播放重復次數 android:repeatMode setRepeatMode(int) 設置動畫重復的方式(當repeat count>0時才有效) “reverse“(2) or “restart“(1) android:startOffset setStartOffset(long) 設置動畫開始播放的延遲時間 android:fillAfter setFillAfter(boolean) 設置為true時,視圖會停留在動畫結束的狀態。 android:fillBefore setFillBefore(boolean) 默認值是true,視圖會停留在動畫開始的狀態 android:fillEnable setFillEnable(boolean) 默認值是false。如果是true,動畫將會應用fillBefore值;否則,fillBefore的值會被忽略,transformation會在動畫結束的時候被應用。 android:detachWallpaper setDetachWallpaper(boolean) 默認值是false。如果為true,並且動畫窗體有一個壁紙的話,那麼動畫只會應用給window,牆紙是靜態不動的 android:zAdjustment setZAdjustment(int) 允許在動畫播放期間,調整播放內容在Z軸方向的順序。”top“(1) or “normal“(0) or “bottom“(-1)

android:zAdjustment:允許在動畫播放期間,調整播放內容在Z軸方向的順序:

normal(0):正在播放的動畫內容保持當前的Z軸順序, top(1):在動畫播放期間,強制把當前播放的內容放到其他內容的上面; bottom(-1):在動畫播放期間,強制把當前播放的內容放到其他內容之下

ScaleAnimation – 縮放動畫

XML屬性名稱 描述 android:fromXScale 動畫起始時,X軸坐標的伸縮尺寸。0.0表示收縮到沒有。1.0表示正常沒伸縮。>1.0表示放大。<1.0表示收縮。 android:toXScale 動畫結束時X軸坐標的伸縮尺寸 android:fromYScale 動畫起始時Y軸坐標的伸縮尺寸 android:toYScale 動畫結束時Y軸坐標的伸縮尺寸 android:pivotX 縮放動畫作用點在X軸方向上的位置。android:pivotX=”50”表示絕對定位,相對於零點偏移50 –> Animation.ABSOLUTE android:pivotX=”50%”表示相對控件本身 –> Animation.RELATE_TO_SELF android:pivotX=”50%p”表示相對控件的父控件 –> Animation.RELATE_TO_PARENT android:pivotY 縮放動畫作用點在Y軸方向上的位置
xml定義縮放動畫

然後通過AnimationUtils類裝載動畫,進行應用。

Animation scaleAnimation =  AnimationUtils.loadAnimation(this, R.anim.scale_anim);
targetIv.startAnimation(scaleAnimation);
代碼定義縮放動畫
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.5f, 1.0f, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(1000);
scaleAnimation.setInterpolator(new OvershootInterpolator());
scaleAnimation.setFillAfter(true);
targetIv.startAnimation(scaleAnimation);

ScaleAnimation有4個構造方法。

public ScaleAnimation(Context context, AttributeSet attrs) {}
public ScaleAnimation(float fromX, float toX, float fromY, float toY) {}
public ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY) {}
public ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {}

第一個構造用於從資源文件中加載資源。我們主要用後三個。後面三個構造的區別就在設置變換中軸點與否。不指定pivotXType和pivotYType的話,默認采用ABSOLUTE形式,pivotX與pivotY的值都是相對於(0,0)左上角偏移的。

pivotXType可設置的參數有三種:ABSOLUTE、RELATE_TO_SELF、RELATE_TO_PARENT。

ABSOLUTE表示當前設置的pivotX和pivotY值是絕對值,相對於左上角偏移。比如:android:pibotX=”50” RELATE_TO_SELF表示設置的pivotX和pivotY是相對值。比如:android:pivotX = “50%”表示X方向中軸點在正中間。 取值范圍是[0% ~ 100%] RELATE_TO_PARENT也是表示相對值,是相對於該視圖的父控件而言。比如:android:pivotX = “50%p”表示X方向中軸點是其父控件的中間位置。取值范圍是[0% ~ 100%]

效果如下:

這裡寫圖片描述


RotateAnimation – 旋轉動畫

XML屬性名稱 描述 android:fromDegrees 動畫起始的角度(可正可負) android:toDegrees 動畫終止的角度(可正可負) android:pivotX 旋轉作用點在X軸方向上的位置。android:pivotX=”50”表示絕對定位,相對於零點偏移50 android:pivotX=”50%”表示相對控件本身 android:pivotX=”50%p”表示相對控件的父控件 android:pivotY 旋轉作用點在Y軸方向上的位置
xml中設置旋轉動畫

代碼中設置旋轉動畫
RotateAnimation rotateAnimation = new RotateAnimation(0.0f, 550.0f, Animation.RELATIVE_TO_SELF, 0.3f, Animation.RELATIVE_TO_SELF, 0.3f);
rotateAnimation.setDuration(1500);
rotateAnimation.setInterpolator(new OvershootInterpolator());
rotateAnimation.setFillAfter(true);
targetIv.startAnimation(rotateAnimation);

效果圖如下:

這裡寫圖片描述


TranslateAnimation – 平移動畫

XML屬性名稱 描述 android:fromXDelta 平移動畫起始位置X軸坐標 android:toXDelta 平移動畫結束位置X軸坐標 android:fromYDelta 平移動畫起始位置Y軸坐標 android:toYDelta 平移動畫結束位置Y軸坐標
xml中設置平移動畫:

代碼中設置平移動畫
TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 0);
translateAnimation.setDuration(1000);
translateAnimation.setInterpolator(new AnticipateOvershootInterpolator());
targetIv.startAnimation(translateAnimation);

這裡寫圖片描述


AlphaAnimation – 漸變動畫

XML屬性名稱 描述 android:fromAlpha 動畫開始時操作對象的alpha值 android:toAlpha 動畫終止時操作對象的alpha值
xml中設置漸變動畫:

代碼中設置漸變動畫:
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.2f);
alphaAnimation.setDuration(1500);
alphaAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
alphaAnimation.setRepeatMode(Animation.REVERSE);
alphaAnimation.setRepeatCount(1);
targetIv.startAnimation(alphaAnimation);

這裡寫圖片描述


AnimationSet – 動畫集合

上面都是一個個的單獨的動畫,我們可以將很多個單獨的動畫組合到一起成為一個集合。

動畫集合也可以在xml中設置。需要用標簽包括其他簡單的動畫。比上述公共動畫屬性多了一個android:shareInterpolator=”boolean”,表示是否對子動畫設置相同的插值器。

xml中設置set動畫集合


    
    
    
    


我們來看一個現象:
設置了上面的set動畫之後,開始運行的時候,會發現漸變動畫開始運行的時候,會先變小,旋轉一個角度,然後才開始動畫。仔細分析xml代碼之後,發現是在動畫開始的時候,把scale和rotate中的初始狀態給應用了。這時候想起了android:fillBefore屬性。
然後在scale、rotate 動畫裡添加了android:fillBefore=”false”屬性之後,發現還是不好使。查看Animation類的源碼發現,fillBefore必須在設置fillEnable=”true”的時候才神效。並且fillBefore的默認值是true,所以才會出現上述情況。

再次修改之後,代碼如下:



    
    
    
    

效果如下:

這裡寫圖片描述


Frame動畫

Frame動畫就是把圖片一幀一幀的播放出來的顯示效果,類似於gif圖片。幀動畫設置很簡單,只需要把每一幀對應的圖片按照順序添加進去,然後設置每一幀的顯示時長,然後為view控件設置該動畫,播放就行了。

xml設置幀動畫:


    
    
    
    
    
    
    

給ImageView設置動畫:

targetIv.setBackgroundResource(R.drawable.frame_anim);
AnimationDrawable animationDrawable = (AnimationDrawable) targetIv.getBackground();
animationDrawable.start();
代碼中設置幀動畫
AnimationDrawable animationDrawable = new AnimationDrawable();
animationDrawable.setOneShot(false);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.person1), 200);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.person2), 200);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.person3), 200);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.person4), 200);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.person5), 200);
targetIv.setImageDrawable(animationDrawable);
animationDrawable.start();

這裡寫圖片描述


LayoutAnimationController

Tween Animation和Frame Animation都是針對單個view操作的。而LayoutAnimationController可以針對一個ViewGroup進行動畫操作,可以讓一組view的每個view按照一定的規則展示動畫。
比如:可以針對listView、gridView或者recyclerView,定義item的出場動畫,而不是非常死板的一下子全顯示出來。

一般對ListView使用layoutAnimation動畫,對GridView使用gridLayoutAnimation動畫。對RecyclerView來說,正常情況下只能使用layoutAnimation動畫,應用gridLayoutAnimation動畫的時候會報錯。不錯可以針對RecyclerView生成一個子類做一下處理進行支持gridLayoutAnimation動畫。

xml屬性 對應的方法 描述 android:delay setDelay(float) 動畫播放的延遲時間 android:animationOrder setOrder(int) 子view播放動畫的順序 [ normal android:interpolator setInterpolator(Interpolator) setIntepolator(Context, @InterpolatorRes int) 插值器 android:animation LayoutAnimationController(animation) 指定子view的動畫
xml定義LayoutAnimation


    
    
代碼中定義LayoutAnimation
public LayoutAnimationController(Animation animation) {
    this(animation, 0.5f);
}
public LayoutAnimationController(Animation animation, float delay) {
    mDelay = delay;
    setAnimation(animation);
}

LayoutAnimationController有三個構造函數,常用的時候上面兩個。delay表示每個子view啟動動畫的延遲時間,默認是0.5f。delay以秒為單位

Animation animation = AnimationUtils.loadAnimation(this, R.anim.item_list_anim);
        LayoutAnimationController layoutAnimationController = new LayoutAnimationController(animation);
        layoutAnimationController.setInterpolator(new AccelerateInterpolator());
        layoutAnimationController.setDelay(0.5f);
        layoutAnimationController.setOrder(LayoutAnimationController.ORDER_RANDOM);
        recyclerView.setLayoutAnimation(layoutAnimationController);

這裡寫圖片描述


GridLayoutAnimationController

GridLayoutAnimationController是LayoutAnimationController的子類。針對GridView做動畫操作。

xml屬性 對應的方法 描述 android:delay setDelay(float) 動畫播放的延遲時間 android:columnDelay setColumnDelay(float) 列播放動畫的延遲時間 android:rowDelay setRowDelay(float) 行播放動畫的延遲時間 android:animationOrder setOrder(int) 子view播放動畫的順序 [ normal android:animation LayoutAnimationController(animation) 指定子view的動畫
xml中定義

代碼中設置

同樣是使用一個Animation構造出GridLayoutAnimation對象,然後設置各種參數,最後設置此動畫GridView即可。

Animation animation = AnimationUtils.loadAnimation(this, R.anim.item_anim_alpha);
GridLayoutAnimationController gridLayoutAnimationController = new GridLayoutAnimationController(animation);
gridLayoutAnimationController.setDirection(GridLayoutAnimationController.DIRECTION_BOTTOM_TO_TOP | GridLayoutAnimationController.DIRECTION_RIGHT_TO_LEFT);
gridLayoutAnimationController.setDirectionPriority(GridLayoutAnimationController.PRIORITY_ROW);
gridRecyclerView.setLayoutAnimation(gridLayoutAnimationController);

效果如下:

這裡寫圖片描述


RecyclerView擴展

正常情況下,我們可以對RecyclerView使用LayoutAnimation動畫。但是如果對RecycleView使用動畫的時候出現以下錯誤:

AndroidRuntime: FATAL EXCEPTION: main                                                       
Process: com.jacksen.demo.view, PID: 30770                                                  
java.lang.ClassCastException: android.view.animation.LayoutAnimationController$AnimationParameters cannot be cast to android.view.animation.GridLayoutAnimationController$AnimationParameters
                   at android.view.animation.GridLayoutAnimationController.getDelayForView(GridLayoutAnimationController.java:299)
                   at android.view.animation.LayoutAnimationController.getAnimationForView(LayoutAnimationController.java:321)
                   at android.view.ViewGroup.bindLayoutAnimation(ViewGroup.java:3717)
                   at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2892)
                   ......

意思就是GridLayoutAnimationController.AnimationParameters不能強轉成LayoutAnimationController.AnimationParameters。

RecyclerView的出現本來就是替代Listview的,但是它有可以展示出GridView的效果,但是怎麼讓RecyclerView設置GridLayoutManager的時候應用gridLayoutAnimation動畫呢?

我們先來看看Gridview怎麼實現的?

在GridView源碼裡面搜索”LayoutAnimation”關鍵字發現,只有一個attachLayoutAnimationParameters()的函數,裡面將layoutAnimationParameters強轉成GridLayoutAnimationController.AnimationParameters。

@Override
    protected void attachLayoutAnimationParameters(View child,
            ViewGroup.LayoutParams params, int index, int count) {
        GridLayoutAnimationController.AnimationParameters animationParams =
                (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
        if (animationParams == null) {
            animationParams = new GridLayoutAnimationController.AnimationParameters();
            params.layoutAnimationParameters = animationParams;
        }
        animationParams.count = count;
        animationParams.index = index;
        animationParams.columnsCount = mNumColumns;
        animationParams.rowsCount = count / mNumColumns;
        if (!mStackFromBottom) {
            animationParams.column = index % mNumColumns;
            animationParams.row = index / mNumColumns;
        } else {
            final int invertedIndex = count - 1 - index;
            animationParams.column = mNumColumns - 1 - (invertedIndex % mNumColumns);
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / mNumColumns;
        }
    }

然後就想到去RecyclerView中去找attachLayoutAnimationParameters()方法,但是沒有,其父類ViewGroup裡面有此方法:

protected void attachLayoutAnimationParameters(View child,
            LayoutParams params, int index, int count) {
        LayoutAnimationController.AnimationParameters animationParams =
                    params.layoutAnimationParameters;
        if (animationParams == null) {
            animationParams = new LayoutAnimationController.AnimationParameters();
            params.layoutAnimationParameters = animationParams;
        }
        animationParams.count = count;
        animationParams.index = index;
    }

由此可見RecyclerView默認實現了ViewGroup的LayoutAnimation。我們在RecyclerView中將此方法重寫一下。不過要將mStackFromButtom參數的判斷去掉

public class GridRecyclerView extends RecyclerView {
    public GridRecyclerView(Context context) {
        super(context);
    }
    public GridRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);
    }
    @Override
    public void setLayoutManager(LayoutManager layout) {
        if (layout instanceof GridLayoutManager) {
            super.setLayoutManager(layout);
        } else {
            throw new ClassCastException("you should only use the GridLayoutManager as LayoutManager when you use this " + this.getClass().getSimpleName() + " class");
        }
    }
    @Override
    protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count) {
        if (getLayoutManager() != null && getLayoutManager() instanceof GridLayoutManager) {
            GridLayoutAnimationController.AnimationParameters animationParams =
                    (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
            if (animationParams == null) {
                animationParams = new GridLayoutAnimationController.AnimationParameters();
                params.layoutAnimationParameters = animationParams;
            }
            int mNumColumns = ((GridLayoutManager) getLayoutManager()).getSpanCount();
            animationParams.count = count;
            animationParams.index = index;
            animationParams.columnsCount = mNumColumns;
            animationParams.rowsCount = count / mNumColumns;
            final int invertedIndex = count - 1 - index;
            animationParams.column = mNumColumns - 1 - (invertedIndex % mNumColumns);
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / mNumColumns;
        } else {
            super.attachLayoutAnimationParameters(child, params, index, count);
        }
    }
}

當我們使用GridLayoutManager的時候,不能使用此屬性。

/**
     * stackFromEnd is not supported by GridLayoutManager. Consider using
     * {@link #setReverseLayout(boolean)}.
     */
    @Override
    public void setStackFromEnd(boolean stackFromEnd) {
        if (stackFromEnd) {
            throw new UnsupportedOperationException(
                    "GridLayoutManager does not support stack from end."
                            + " Consider using reverse layout");
        }
        super.setStackFromEnd(false);
    }

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved