Android L帶來了許多新特性,其中就包括了大量的動畫效果,你可以在自己的應用中使用。本文中我將詳解這些動畫和如何在應用中使用。本文中的所有程式碼可以在github上找到。
波紋和強調
現在安卓支援少量的預定義樣式屬性,不過只限於應用特定部分例如狀態列、導航欄。新系統允許我們採用簡單的方式即可獲得兩種非常簡潔、有用的動畫:波紋和被強調的UI元件。波紋是一種給予使用者在UI上動作極好的迴應效果,並且可以自定義波紋的顏色,只需要給colorControlHighlight 屬性設定色值。
1 |
<item name="android:colorControlHighlight">#0000AA</item> |
一樣簡單,很多UI元件像核取方塊都可以使用styles資料夾中的colorAccent 屬性來設定符合你應用主題的顏色,而不必使用不同的圖片和狀態選擇器。
1 |
<item name="android:colorAccent">#00FF00</item> |
圓形顯示
安卓中一件常見工作就是改變螢幕上某一元素的可見性。現在開發者又多了一個選擇可以讓這項工作完成的更加漂亮:圓形顯示。使用ViewAnimationUtil.createCircularReveal方法然後通過動畫監聽在合適的時間改變View的可見性。這有兩個相似的方法用來讓View可見或者消失。注意顯示方法有一個持續時間的設定,以便完成動畫的顯示,隱藏動畫執行的更快一點。getX()和getY()方法返回圖片所在X、Y軸的中心點的座標,getRadius()方法就是返回View的寬度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
private void hideImageCircular() { int x = getX(); int y = getY(); int radius = getRadius(); ValueAnimator anim = ViewAnimationUtils.createCircularReveal(mImageView, x, y, radius, 0); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mImageView.setVisibility( View.INVISIBLE ); } }); anim.start(); } private void revealImageCircular() { int x = getX(); int y = getY(); int radius = getRadius(); ValueAnimator anim = ViewAnimationUtils.createCircularReveal(mImageView, x, y, 0, radius); anim.setDuration( 1000 ); anim.addListener( new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); mImageView.setVisibility( View.VISIBLE ); } }); anim.start(); } |
Activity轉場動畫
除了前述幾種動畫,android L還增加了一些activity的過渡動畫——爆炸、滑動、淡入淡出,讓應用更加平滑。使用這些動畫,你必須在進入和退出activity都要求使用這些內容轉場特效,並且在setOncontent()方法之前。
1 |
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); |
也支援通過樣式設定轉換動畫,我將詳解如何在activity中通過樣式設定專場效果。使用getWindow().setExitTransition( Transition ) 和getWindow().setEnterTransition( Transition ) 方法告訴activity當開啟和關閉時如何執行。使用迸發動畫,從MainActivity和ListFragment 到第二個activity的程式碼可以這樣寫:
1 2 3 4 |
ListFragment: getActivity().getWindow().setExitTransition( new Explode() ); intent = new Intent( getActivity(), ExplodeAnimationActivity.class ); startActivity( intent ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ExplodeAnimationActivity: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); getWindow().setEnterTransition( new Explode() ); getWindow().setExitTransition( new Explode() ); setContentView(R.layout.activity_explode_animation); } @Override public void onBackPressed() { super.onBackPressed(); finishAfterTransition(); } |
注意onBackPressed()方法——這很重要。因為它讓作業系統知道在關閉第二個activity之前要完成動畫的執行。使用這些簡單的程式碼,我們就擁有了三種activity專場動畫。
雖然我沒有用到這個非常有用的工具,但是應當引起注意的是專場動畫監聽——Transition.TransitionListener。使用監聽可以在進入和退出動畫生命週期中的特定點執行一些操作,給你更多讓應用如何表現的控制權。為了防止內容洩漏,需要在Ondestory()方法中移除監聽。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
getWindow().getEnterTransition().addListener( new Transition.TransitionListener { @Override public void onTransitionStart(Transition transition) { } @Override public void onTransitionEnd(Transition transition) { } @Override public void onTransitionCancel(Transition transition) { } @Override public void onTransitionPause(Transition transition) { } @Override public void onTransitionResume(Transition transition) { } }); |
Activity專場動畫之元素共享
除了標準的過渡動畫,現在支援在兩個activity過渡動畫過程中共享元素。第一件要做的就是設定activity,使用內容轉場動畫並且允許覆蓋轉場動畫。可以通過樣式來設定:
1 2 3 |
<item name="android:windowContentTransitions">true</item> <item name="android:windowAllowEnterTransitionOverlap">true</item> <item name="android:windowAllowExitTransitionOverlap">true</item> |
元素共享動畫也可以在程式碼中設定,此例中,我將使用樣式來完成:
1 2 |
<item name="android:windowSharedElementEnterTransition">@transition/changebounds</item> <item name="android:windowSharedElementExitTransition">@transition/changebounds</item> |
Changebounds轉場動畫被定義以xml檔案的形式存放在資原始檔中。注意,我增加了兩位兩個屬性:時長和插值器,讓動畫更有趣。
1 2 3 4 5 |
changebounds.xml: <changeBounds xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:interpolator/bounce" /> |
下一步就是,確定在兩個activity的佈局檔案中都使用實現Comparable介面的View型別(此例中使用ImageView),並且他們的viewName 屬性值必須相同。在第一個activity中具有共享元素的View是這樣的:
1 2 3 4 5 |
<ImageView android:id="@+id/image" android:viewName="image" android:layout_width="match_parent" android:layout_height="250dp" /> |
第二個activity中的是這樣的:
1 2 3 4 5 6 |
<ImageView android:id="@+id/image" android:layout_alignParentBottom="true" android:viewName="image" android:layout_width="match_parent" android:layout_height="250dp" /> |
既然所有的xml設定都準備好了,我們可以開始在執行動畫的activity中寫程式碼了。在第一個activity中我們設定父類容器檢視setTransitionGroup為false,然後獲取我們想要共享元素的ImageView的drawable,把它轉變為位元組流物件放到intent的bundle中。然後,為ImageView使用場景動畫轉換建立ActivityOptions 物件,開啟下一個activity。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Intent intent = new Intent( this, SharedElementSecondAnimationActivity.class ); ((ViewGroup) mImageView.getParent()).setTransitionGroup( false ); ByteArrayOutputStream stream = new ByteArrayOutputStream(); ( (BitmapDrawable) mImageView.getDrawable() ).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); intent.putExtra( "image", stream.toByteArray() ); ActivityOptions options; try { options = ActivityOptions.makeSceneTransitionAnimation( this, mImageView, "image" ); } catch( NullPointerException e ) { Log.e( "SharedElementAnimationChangeBoundsActivity", "Did you set your ViewNames in the layout file?" ); return; } if( options == null ) { Log.e("sharedelementanimation", "Options is null. Something broke. Good luck!"); } else { startActivity(intent, options.toBundle()); } |
在第二個activity中,從Intent的bundle中讀取位元組流並解碼為bitmap物件,然後把它設定到Imageview上。第二個activity也重寫了onBackPressed()方法,為了當返回鍵按下的時候執行退出動畫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_shared_element_second_animation); mImageView = (ImageView) findViewById( R.id.image ); byte[] byteArray = getIntent().getByteArrayExtra("image"); Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); mImageView.setImageBitmap(bitmap); } @Override public void onBackPressed() { super.onBackPressed(); finishAfterTransition(); } |
基於以上這些我們完成了共享元素轉換動畫,在第二個activity中共享元素被移動到新的位置,並且有一個回彈效果。
這幾個動畫的例子僅是android L的冰山一角,而且沒有涉及Kit Kat中引入的場景動畫。我希望這篇教程幫助其他開發者學習到新的東西,使用動畫讓應用不但好看而且有趣。