下面翻譯transition爲「過渡」,強調動畫過程的含義,不過更多時候使用transition單詞自己。
Android 4.4.2 (API level 19) 引入了過渡框架
,它用來在兩個view hierarchies(就是ViewGroup實例)切換時執行改變更畫。它經過動態修改views對象的某些property值來實現動畫,實際上就是用的屬性動畫
。框架內建了一些transition效果,也能夠自定義。而且能夠監聽transition執行過程/生命週期(Lifecycle callbacks)各類回調。java
在UI改變時——view hierarchy(視圖層級) 發生變化,過渡框架對包含的部分改變了的view對象執行屬性動畫
來表達這樣的視覺提示(visual cues)。android
過渡框架包括下面這些特性:數據結構
動畫組Group-level animations
視圖層級發生變化時,能夠同時執行多個動畫,它們造成動畫組,相似AnimationSet
。app
Transition-based animation
Runs animations based on the changes between starting and ending view property values.框架
Built-in animations
Includes predefined animations for common effects such as fade out or movement.ide
Resource file support 支持資源文件方式的使用
Loads view hierarchies and built-in animations from layout resource files.佈局
Lifecycle callbacks
Defines callbacks that provide finer control over the animation and hierarchy change process.動畫
下圖是過渡框架涉及的相關概念和之間的關係:ui
Scene
一個Scene表示一個view hierarchy,存儲對應的views及其property值。
一個Scene必須關聯到一個scene-root
,它是一個ViewGroup,正是對應view hierarchy要添加到的容器。
Scene能夠從layout文件建立(只能是整個佈局文件對應的view hierarchy),或者代碼生成的View/ViewGroup。
若是僅指定目標Scene,那麼框架使用當前scene-root對應的view hierarchy就做爲開始的Scene。this
Transition
表示兩個Scene之間的過渡動畫,它保存了使用到的animations。
TransitionManager
用來執行專場效果。
TransitionListener
用來實現對過渡過程的監聽。
SurfaceView非UI線程不保證同步;
AdapterView管理childView行爲衝突;
TextView改變尺寸時顯示問題;
TextureView非預期效果;
過渡涉及startScene和endScene,它們使用同一個sceneRoot,也就是變化的view hierarchy是在一個ViewGroup下的。
startScene能夠不指定,那麼就默認是當前view hierarchy。
若是連續執行多個Scene以前的切換,那麼上一個endScene就是當前的startScene。
能夠經過代碼或layout文件建立Scene。
Scene mScene; // Obtain the scene root element mSceneRoot = (ViewGroup) mSomeLayoutElement; // Obtain the view hierarchy to add as a child of // the scene root when this scene is entered mViewHierarchy = (ViewGroup) someOtherLayoutElement; // Create a scene mScene = new Scene(mSceneRoot, mViewHierarchy);
/** * Returns a Scene described by the resource file associated with the given * <code>layoutId</code> parameter. If such a Scene has already been created for * the given <code>sceneRoot</code>, that same Scene will be returned. * This caching of layoutId-based scenes enables sharing of common scenes * between those created in code and those referenced by {@link TransitionManager} * XML resource files. * * @param sceneRoot The root of the hierarchy in which scene changes * and transitions will take place. * @param layoutId The id of a standard layout resource file. * @param context The context used in the process of inflating * the layout resource. * @return The scene for the given root and layout id */ public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context);
NOTE:
The framework creates the scene from the entire view hierarchy in the file; you can not create a scene from part of a layout file.
使用Scene.setExitAction() or Scene.setEnterAction() ,傳遞定義的Runnable實例。
能夠執行一些操做,好比修改ListView等。
Note:
Do not use scene actions to pass data between views in the starting and ending scenes. For more information, see Defining Transition Lifecycle Callbacks.
建立好startScene、endScene後,在改變UI的邏輯的地方,就能夠執行Transition來開啓專場動畫了。
動畫文件
的機制相似。如res/transition/fade_transition.xml:
<fade xmlns:android="http://schemas.android.com/apk/res/android" />
而後加載它:
Transition mFadeTransition = TransitionInflater.from(this). inflateTransition(R.transition.fade_transition);
Transition mFadeTransition = new Fade();
預提供的類型有ChangeBounds, ChangeClipBounds, ChangeImageTransform, ChangeTransform, TransitionSet, Visibility
詳見API文檔。
使用下面的方法TransitionManager.go()
:
/** * Convenience method to simply change to the given scene using * the given transition. * * <p>Passing in <code>null</code> for the transition parameter will * result in the scene changing without any transition running, and is * equivalent to calling {@link Scene#exit()} on the scene root's * current scene, followed by {@link Scene#enter()} on the scene * specified by the <code>scene</code> parameter.</p> * * @param scene The Scene to change to * @param transition The transition to use for this scene change. A * value of null causes the scene change to happen with no transition. */ public static void go(Scene scene, Transition transition)
它有一個不含參數transition的重載,默認會使用AutoTransition
。
默認狀況下Transition的執行是針對Scene關聯的view hierarchy中的全部views執行的。
能夠經過Transition的方法removeTarget()、addTarget()來指定它影響的view。
好比若ListView不能經過框架正常完成過渡效果,那麼移除它。
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:transitionOrdering="sequential"> <fade android:fadingMode="fade_out" /> <changeBounds /> <fade android:fadingMode="fade_in" /> </transitionSet>
對應一個TransitionSet ,它是Transition的子類。
若是UI在變化先後差距很小,例如就是同一個ViewGroup的簡單的addView()/removeView()那麼,此時能夠不去執行像startScene、endScene那樣的變化,使用delayed transition
來在view hierarchy變化的時候開啓過渡:
// Start recording changes to the view hierarchy TransitionManager.beginDelayedTransition(mRootView, mFade); // Add the new TextView to the view hierarchy mRootView.addView(mLabelText); // When the system redraws the screen to show this update, // the framework will animate the addition as a fade in
調用public Transition addListener (Transition.TransitionListener listener)
。
回調方法見Transition.TransitionListener
。
A custom transition, like one of the built-in transition types, applies animations to child views of both the starting and ending scenes. Unlike built-in transition types, however, you have to provide the code that captures property values and generates animations. You may also want to define a subset of target views for your animation.
public class CustomTransition extends Transition { @Override public void captureStartValues(TransitionValues values) {} @Override public void captureEndValues(TransitionValues values) {} @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {} }
方法captureStartValues()和captureEndValues()用來獲取startScene、endScene關聯的view的property值。能夠對關心的view的屬性值進行記錄。
相似下面這樣:
public class CustomTransition extends Transition { // Define a key for storing a property value in // TransitionValues.values with the syntax // package_name:transition_class:property_name to avoid collisions private static final String PROPNAME_BACKGROUND = "com.example.android.customtransition:CustomTransition:background"; @Override public void captureStartValues(TransitionValues transitionValues) { // Call the convenience method captureValues captureValues(transitionValues); } // For the view in transitionValues.view, get the values you // want and put them in transitionValues.values private void captureValues(TransitionValues transitionValues) { // Get a reference to the view View view = transitionValues.view; // Store its background property in the values map transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground()); } ... }
TransitionValues是一個很是簡單的數據結構,它保存和view關聯的屬性值,startScene和endScene分別使用一個TransitionValues對象來記錄view hierarchy的各個view的狀態。
要記錄的「property_name」須要保存惟一,由於可能和其它Transition衝突,建議格式是:
package_name:transition_name:property_name
。
過渡框架中的Transition使用屬性動畫做爲動畫機制的實現。
子自定義Transition重寫createAnimator()方法來提供須要的動畫邏輯,工做就是根據startValues和endValues來建立Animator。
針對有些view的過渡只在endScene或者startScene中有,因此startValues和endValues可能爲null。
框架會爲每個動畫的view建立一個Animator,也就是調用一次createAnimator()。
下面是一個案例ChangeColor.java:
public class ChangeColor extends Transition { /** Key to store a color value in TransitionValues object */ private static final String PROPNAME_BACKGROUND = "customtransition:change_color:background"; // BEGIN_INCLUDE (capture_values) /** * Convenience method: Add the background Drawable property value * to the TransitionsValues.value Map for a target. */ private void captureValues(TransitionValues values) { // Capture the property values of views for later use values.values.put(PROPNAME_BACKGROUND, values.view.getBackground()); } @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); } // Capture the value of the background drawable property for a target in the ending Scene. @Override public void captureEndValues(TransitionValues transitionValues) { captureValues(transitionValues); } // END_INCLUDE (capture_values) // BEGIN_INCLUDE (create_animator) // Create an animation for each target that is in both the starting and ending Scene. For each // pair of targets, if their background property value is a color (rather than a graphic), // create a ValueAnimator based on an ArgbEvaluator that interpolates between the starting and // ending color. Also create an update listener that sets the View background color for each // animation frame @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { // This transition can only be applied to views that are on both starting and ending scenes. if (null == startValues || null == endValues) { return null; } // Store a convenient reference to the target. Both the starting and ending layout have the // same target. final View view = endValues.view; // Store the object containing the background property for both the starting and ending // layouts. Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND); Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND); // This transition changes background colors for a target. It doesn't animate any other // background changes. If the property isn't a ColorDrawable, ignore the target. if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) { ColorDrawable startColor = (ColorDrawable) startBackground; ColorDrawable endColor = (ColorDrawable) endBackground; // If the background color for the target in the starting and ending layouts is // different, create an animation. if (startColor.getColor() != endColor.getColor()) { // Create a new Animator object to apply to the targets as the transitions framework // changes from the starting to the ending layout. Use the class ValueAnimator, // which provides a timing pulse to change property values provided to it. The // animation runs on the UI thread. The Evaluator controls what type of // interpolation is done. In this case, an ArgbEvaluator interpolates between two // #argb values, which are specified as the 2nd and 3rd input arguments. ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor.getColor(), endColor.getColor()); // Add an update listener to the Animator object. animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Object value = animation.getAnimatedValue(); // Each time the ValueAnimator produces a new frame in the animation, change // the background color of the target. Ensure that the value isn't null. if (null != value) { view.setBackgroundColor((Integer) value); } } }); // Return the Animator object to the transitions framework. As the framework changes // between the starting and ending layouts, it applies the animation you've created. return animator; } } // For non-ColorDrawable backgrounds, we just return null, and no animation will take place. return null; } // END_INCLUDE (create_animator) }
自定義Transition的使用和內建的Fade、ChangeBounds等是徹底相同的。
(本文使用Atom編寫)