Android動畫系列(1)—幀動畫

首語

  • 這是Android動畫系列的目錄,有興趣的可以學習:Android動畫
  • 在某些情況下,圖片需要在屏幕上呈現動畫效果。如果您希望顯示由多張圖片組成的自定義加載動畫,或者希望一個圖標在用戶執行操作後變爲另一個圖標,這種做法就非常實用。Android 提供了兩個選項可以實現。
  • 第一個選項是使用 AnimationDrawable。使用該選項,您可以指定多個靜態圖片資源(每次展示一個)來創建動畫。第二個選項是使用 AnimatedVectorDrawable。使用該選項,您可以爲矢量圖添加動畫效果。

幀動畫(AnimationDrawable)

  • 幀動畫就是由N張靜態圖片,然後通過控制依次顯示這些圖片,就形成了動畫。
  • 實現幀動畫有兩種方式,第一種是xml中實現,第二種是代碼實現。
XML實現
  • 幀動畫的XML文件位置在res/drawable/ 目錄中,XML 文件包含一個 <animation-list> 元素(用作根節點)和一系列子 <item> 節點(每個節點定義一個幀)。
	<!--android:oneshot="true"表示是否循環播放,true表示只播放一次。-->
    <!--android:duration="200"表示這張圖片動畫播放的時長,單位ms。-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">

    <item android:drawable="@drawable/lockscreen_01" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_02" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_03" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_04" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_05" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_06" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_07" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_08" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_09" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_10" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_11" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_12" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_13" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_14" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_15" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_16" android:duration="200"/>
    <item android:drawable="@drawable/lockscreen_17" android:duration="200"/>

</animation-list>
  • 在Activity中,該動畫添加到了ImageView中。特別要 注意 的是:AnimationDrawablestart()方法不能在Activity的onCreate()方法中調用,因爲AnimationDrawable有可能在加載的時候還沒有完全加載到Window上,所以最好的使用時機是onWindowFocusChanged()方法中。
		daiv.setBackgroundResource(R.drawable.drawable_animation);
        animationDrawable = (AnimationDrawable) daiv.getBackground();
        animationDrawable.start();
代碼實現
public void animationDrawable() {
        //創建一個AnimationDrawable
        AnimationDrawable animationDrawable1 = new AnimationDrawable();
        //準備好資源圖片
        int[] ids = {R.drawable.lockscreen_01, R.drawable.lockscreen_02, R.drawable.lockscreen_03, R.drawable.lockscreen_04, R.drawable.lockscreen_05};
        //通過for循環添加每一幀動畫
        for (int i = 0; i < ids.length; i++) {
            Drawable frame = getResources().getDrawable(ids[i]);
            //設定時長
            animationDrawable1.addFrame(frame, 200);
        }
        animationDrawable1.setOneShot(false);
        //將動畫設置到背景上
        daiv.setBackground(animationDrawable1);
        //開啓幀動畫
        animationDrawable1.start();
    }
在指定地方播放幀動畫
public class Frame extends android.support.v7.widget.AppCompatImageView {

    private AnimationDrawable anim;

    public Frame(Context context) {
        super(context);
    }

    public Frame(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public Frame(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setAnim(AnimationDrawable anim) {
        this.anim = anim;
    }

    public void setLocation(int top, int left) {
        this.setFrame(left, top, left + 200, top + 200);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        try {
            Field mCurFrame = AnimationDrawable.class.getDeclaredField("mCurFrame");
            mCurFrame.setAccessible(true);
            int frameInt = mCurFrame.getInt(anim);
            if (frameInt == anim.getNumberOfFrames() - 1) {
                setVisibility(INVISIBLE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        super.onDraw(canvas);
    }
}
		FrameLayout frameLayout = new FrameLayout(this);
        setContentView(frameLayout);
        final Frame frame = new Frame(this);
        frame.setBackgroundResource(R.drawable.drawable_animation);//animation-list
        frame.setVisibility(View.INVISIBLE);
        final AnimationDrawable anim = (AnimationDrawable) frame.getBackground();
        frame.setAnim(anim);
        frameLayout.addView(frame);
        frameLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //設置按下才產生動畫效果
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    anim.stop();
                    float x = event.getX();
                    float y = event.getY();
                    frame.setLocation((int) y - 40, (int) x - 20);
                    frame.setVisibility(View.VISIBLE);
                    anim.start();
                }
                return false;
            }
        });
實現效果

AnimatedVectorDrawable

  • 矢量圖是一種無需像素化或進行模糊處理即可縮放的圖片。藉助 AnimatedVectorDrawable 類(以及用於實現向後兼容的 AnimatedVectorDrawableCompat),您可以爲矢量圖添加動畫效果,例如旋轉或更改路徑數據以將其變爲其他圖片。不過在學習AnimatedVectorDrawable之前,我們有必要從VectorDrawable(矢量圖)開始,因爲熟悉了矢量圖,纔可以添加動畫。
VectorDrawable
  • VectorDrawable 是一種矢量圖形,在 XML 文件中定義爲一組點、線條和曲線及其相關顏色信息。使用矢量圖的主要優勢在於圖片可縮放。您可以在不降低顯示質量的情況下縮放圖片,也就是說,可以針對不同的屏幕密度調整同一文件的大小,而不會降低圖片質量。這不僅能縮減 APK 文件大小,還能減少開發者維護工作。您還可以對動畫使用矢量圖片,具體方法是針對各種顯示屏分辨率使用多個 XML 文件,而不是多張圖片。
  • AndroidStudio中也提供了一些VectorDrawable,使用步驟如下:
  • File—>new—>Vector Asset;打開界面如圖所示,點擊Clip Art右邊的圖標可以選擇Android自帶的VectorDrawable,同時也可以從外部選擇SVG,PSD格式的圖片生成VectorDrawable。點擊next,finish。圖片的XML代碼如下。
  • 這裏我們特別說明一下SVG(Scalable Vector Graphics),意爲可縮放矢量圖,SVG基於可擴展標記語言,使用XML格式定義圖形,與其它圖像格式相比,使用SVG的優勢在於:
    • SVG 可被非常多的工具讀取和修改(比如記事本)。
    • SVG 與 JPEG 和 GIF 圖像比起來,尺寸更小,且可壓縮性更強。
    • SVG 是可伸縮的。
    • SVG 圖像可在任何的分辨率下被高質量地打印。
    • SVG 可在圖像質量不下降的情況下被放大。
    • SVG 圖像中的文本是可選的,同時也是可搜索的(很適合製作地圖)。
    • SVG 可以與 Java 技術一起運行。
    • SVG 是開放的標準。
    • SVG 文件是純粹的 XML。
<vector android:height="24dp" android:tint="#FF0006"
    android:viewportHeight="24.0" android:viewportWidth="24.0"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#FF000000" android:pathData="M 0,0 L50,100 L100,0Z"/>
</vector>
  • 看到這個是不是直接很懵逼, android:viewportHeight定義矢量圖視圖的高度,視圖就是矢量圖path路徑數據所繪製的虛擬畫布。android:viewportWidth定義矢量圖視圖的寬度。畫布寬高均爲24,意味着繪製了一個24*24的畫布,path路徑必須在這個畫布大小裏去繪製,超出畫布就不顯示了。android:fillColor定義填充路徑的顏色,如果沒有定義則不填充路徑。最難理解的就是android:pathData,它的值是一系列字母和數字,爲了告訴解析器如何繪製的,這些字母代表一些指令,具體如下:

    • M(m):相當於Path.moveTo(),開始新一段的path。
    • L(l):相當於Path.lineTo(),移動到指定的點。
    • H(h):水平移動。
    • V(v):豎直移動。
    • C©:三階貝塞爾曲線。相當於Path.cubicTo()。
    • S(s):同C,但比C要更平滑。
    • Q(q):二階貝塞爾曲線
    • T(t):同Q,但比q平滑。
    • A(a):弧線。相當於Path.arcTo()。
    • Z(z):關閉。相當於Path.close()。
  • 每一個指令都有大小寫,大寫表示絕對定位,小寫表示相對定位(相對定位參考點是上一次座標)。 由於繪製路徑的複雜性,因此強烈建議您使用 SVG 編輯器來創建複雜的圖形。

  • android:pathData="M 0,0 L50,100 L100,0Z"表示座標(0,0)lineto(50,100)lineto(100,0)關閉,繪製出來是一個等腰三角形。

Vector語法
<vector>
<!--<group>用來把多個<path>組合在一起,進行相同的處理。-->
    <group>
        <path>
        <path>
    </group>
</vector>

——關於VectorDrawable的介紹就到這裏!

使用 AnimatedVectorDrawable
  • 我們通常需要在三個XML文件中定義添加動畫效果後的矢量圖:
    • 矢量圖,其中位於res/drawable/
    • 添加動畫效果的矢量圖,其中 位於 res/drawable/
    • 一個或多個Animator,其中 元素位於 res/animator/
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100">
    <group
        android:name="group">
        <path android:name="tangle"
            android:strokeColor="@color/colorAccent"
            android:strokeWidth="2"
            android:pathData="M 0,0 L50,100 L100,0Z"/>
    </group>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_drawable">
    <target
        android:animation="@anim/anim_vector"
        android:name="tangle"/>
    <target
        android:animation="@anim/anim_group"
        android:name="group"/>

</animated-vector>
  • 兩個動畫,一個縮放動畫(屬性動畫介紹參考Android動畫目錄),一個針對pathData的動畫。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="5000"
        android:propertyName="scaleX"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueFrom="1"
        android:valueTo="0.5"
        android:valueType="floatType" />
   
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="2000"
        android:propertyName="pathData"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueFrom="M 0,0 L50,100 L100,0Z"
        android:valueTo="M 0,100 L50,0 L100,100Z"
        android:valueType="pathType" />
</set>
  @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        AnimatedVectorDrawable animatedVectorDrawable = (AnimatedVectorDrawable) vectoriv.getDrawable();
        animatedVectorDrawable.start();
    }
aapt合併xml
  • 這裏有很多文件只是爲了讓一個動畫可以繪製!如果矢量圖動畫在其他地方重複使用,這是實現矢量可繪製動畫的最佳方法。如果它們只用於這個動畫矢量繪圖,那麼有一個更緊湊的方法來實現它們。使用AAPT的內聯資源格式,可以在同一個XML文件中定義所有三個資源。因爲我們正在製作一個矢量圖的動畫效果,所以我們將文件放在res/drawable/下。
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="200dp"
            android:height="200dp"
            android:viewportWidth="100"
            android:viewportHeight="100">
            <group android:name="group">
                <path
                    android:name="tangle"
                    android:strokeWidth="2"
           			android:pathData="M 0,0 L50,100 L100,0Z"
                    android:strokeColor="@color/colorAccent" />
            </group>
        </vector>
    </aapt:attr>

    <target android:name="tangle">
        <aapt:attr name="android:animation">
            <objectAnimator
        		android:duration="2000"
       			android:propertyName="pathData"
       			    <!--無限次infinite-->
       			android:repeatCount="infinite"
       			android:repeatMode="reverse"
       			android:valueFrom="M 0,0 L50,100 L100,0Z"
       			android:valueTo="M 0,100 L50,0 L100,100Z"
        		android:valueType="pathType" />

        </aapt:attr>
    </target>

    <target android:name="group">
        <aapt:attr name="android:animation">
            <objectAnimator
        		android:duration="5000"
        		android:propertyName="scaleX"
        		android:repeatCount="infinite"
        		android:repeatMode="reverse"
	        	android:valueFrom="1"
	        	android:valueTo="0.5"
		        android:valueType="floatType" />
        </aapt:attr>
    </target>

</animated-vector>
  • XML標記aapt:attr告訴aapt該標記的子標記應被視爲資源並提取到其自己的資源文件中。使用這種方式更緊湊。實現效果相同。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章