誕生于4.4的transition框架為在不同的UI狀態(tài)之間產(chǎn)生動(dòng)畫(huà)效果提供了非常方便的API。5.0中Activity和Fragment 轉(zhuǎn)場(chǎng)變換也是建立在Transitions框架的新特性之上的。
該框架主要基于兩個(gè)概念:scenes(場(chǎng)景)和transitions(變換)
1. Secene
Transition Framework 核心就是根據(jù)Scene的不同幫助開(kāi)發(fā)者們自動(dòng)生成動(dòng)畫(huà)。
官方文檔
A scene represents the collection of values that various properties in the View hierarchy will have when the scene is applied. A Scene can be configured to automatically run a Transition when it is applied, which will animate the various property changes that take place during the scene change.通俗的解釋就是這個(gè)類存儲(chǔ)著一個(gè)根view下的各種view的屬性。
創(chuàng)建Scene
創(chuàng)建一個(gè) Scene有兩種方法
// sceneRoot是Scene的 Container,也可以說(shuō)是它的根布局
1. Scene.getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) ;
2. new Scene(ViewGroup sceneRoot, View layout);
例子如下:
protected Scene scene1;
protected Scene scene2;
protected void initScene(@IdRes int sceneRoot, @LayoutRes int scene1_layout, @LayoutRes int scene2_layout) {
ViewGroup sceneRoot= (ViewGroup) findViewById(rootView);
scene1= Scene.getSceneForLayout(sceneRoot,scene1_layout,this);
scene2=Scene.getSceneForLayout(sceneRoot,scene2_layout,this);
TransitionManager.go(scene1); //先把初始狀態(tài)設(shè)置為scene1
}
或者
protected Scene scene1;
protected Scene scene2;
ViewGroup sceneRoot=findViewById(R.id.rootView);
View view1= LayoutInflater.from(this).inflate(R.layout.changeclipbounds_scene,null);
View view2= LayoutInflater.from(this).inflate(R.layout.changeclipbounds_scene,null);
ImageView iv1=view1.findViewById(R.id.imageView);
ImageView iv2=view2.findViewById(R.id.imageView);
iv1.setClipBounds(new Rect(0,0,100,100));
iv2.setClipBounds(new Rect(100,100,200,200));
scene1=new Scene(sceneRoot,view1);
scene2=new Scene(sceneRoot,view2);
TransitionManager.go(scene1);//先把初始狀態(tài)設(shè)置為scene1
sceneRoot 在動(dòng)畫(huà)開(kāi)始時(shí),會(huì)將sceneRoot中的所有子View都remove掉,然后在sceneRoot 中加載我們的end Scene。
所以,對(duì)于end Scene,如果是通過(guò)代碼new Scene(mSceneRoot, view)創(chuàng)建的Scene其實(shí)對(duì)于view是有要求的:view是沒(méi)有parentview的,不然在addview的時(shí)候會(huì)報(bào)錯(cuò)
驗(yàn)證代碼如下:
//測(cè)試該段代碼
LinearLayout container=new LinearLayout(appContext);
View view= LayoutInflater.from(appContext).inflate(R.layout.layout_temp,null);
FrameLayout frameLayout=new FrameLayout(appContext);
frameLayout.addView(view);
container.addView(view);
//log
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4917)
at android.view.ViewGroup.addView(ViewGroup.java:4748)
例子
//scene1.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/bangtang"
tools:layout_editor_absoluteX="128dp"
tools:layout_editor_absoluteY="58dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="58dp"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/shengdanlaoren"
tools:layout_editor_absoluteX="49dp"
tools:layout_editor_absoluteY="226dp"
app:layout_constraintTop_toTopOf="@+id/imageView3"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="49dp" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/xueren"
tools:layout_editor_absoluteX="210dp"
tools:layout_editor_absoluteY="226dp"
android:layout_marginTop="40dp"
app:layout_constraintTop_toBottomOf="@+id/imageView1"
android:layout_marginEnd="46dp"
app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout>
//scene2.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/shengdanlaoren"
tools:layout_editor_absoluteX="128dp"
tools:layout_editor_absoluteY="58dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="58dp"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/xueren"
tools:layout_editor_absoluteX="49dp"
tools:layout_editor_absoluteY="226dp"
app:layout_constraintTop_toTopOf="@+id/imageView1"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="49dp" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/bangtang"
tools:layout_editor_absoluteX="210dp"
tools:layout_editor_absoluteY="226dp"
android:layout_marginTop="40dp"
app:layout_constraintTop_toBottomOf="@+id/imageView2"
android:layout_marginEnd="46dp"
app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout>

Activity代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_scene);
Button btn=findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clickChange();
}
});
ViewGroup sceneRoot= (ViewGroup) findViewById(rootView);
scene1= Scene.getSceneForLayout(sceneRoot,scene1_layout,this);
scene2=Scene.getSceneForLayout(sceneRoot,scene2_layout,this);
TransitionManager.go(scene1);//先把初始狀態(tài)設(shè)置為scene1
}
protected void clickChange(){
TransitionManager.go(isScene1?scene2:scene1,getTransition());
isScene1=!isScene1;
}
Transition getTransition() {
return new ChangeBounds();
}
getTransition方法提供的是兩個(gè)Scene的切換效果,下面會(huì)講到,所以先不管它。
最后出來(lái)的效果是:


根據(jù)效果圖和Id對(duì)比圖可以發(fā)現(xiàn)切換時(shí)是相同id的View之間互相切換,那么如果兩個(gè)Scene之間View的Id不對(duì)等(id不相同 或者 一多一少)呢?

這個(gè)時(shí)候可以試著運(yùn)行一下就會(huì)發(fā)現(xiàn):仍然可以實(shí)現(xiàn)兩個(gè)場(chǎng)景的切換,但是切換過(guò)程沒(méi)有任何動(dòng)畫(huà)效果(即Transition沒(méi)有起作用),就只是簡(jiǎn)單的替換。
上面例子getTransition方法返回ChangeBounds(),其實(shí)就是一種Transition的實(shí)現(xiàn),下面來(lái)詳細(xì)了解一下:
2. transitions
當(dāng)一個(gè)Scene發(fā)生改變時(shí),transition主要負(fù)責(zé):
- 捕捉每個(gè)View在開(kāi)始場(chǎng)景和結(jié)束場(chǎng)景時(shí)的狀態(tài)。
- 根據(jù)兩個(gè)場(chǎng)景(開(kāi)始和結(jié)束)之間的區(qū)別創(chuàng)建一個(gè)
Animator。
2.1 API 21之后,框架層提供的Transitions:
2.1.1 ChangeBounds :檢測(cè)view的位置邊界創(chuàng)建移動(dòng)和縮放動(dòng)畫(huà)
捕獲共享元素的layout bound,然后播放layout bound變化動(dòng)畫(huà)。ChangeBounds 是共享元素變換中用的最多的,因?yàn)榍昂髢蓚€(gè)activity中共享元素的大小和位置一般都是不同的。
根據(jù)始末位置畫(huà)出Path,再根據(jù)Path創(chuàng)造Animator

/**
* This transition captures the layout bounds of target views before and after
* the scene change and animates those changes during the transition.
*
* <p>A ChangeBounds transition can be described in a resource file by using the
* tag <code>changeBounds</code>, using its attributes of
* {@link android.R.styleable#ChangeBounds} along with the other standard
* attributes of {@link android.R.styleable#Transition}.</p>
*/
public class ChangeBounds extends Transition {
...
}
2.1.2 ChangeClipBounds :檢測(cè)view的剪切區(qū)域的位置邊界,和ChangeBounds類似。不過(guò)ChangeBounds針對(duì)的是view而ChangeClipBounds針對(duì)的是view的剪切區(qū)域(setClipBound(Rect rect) 中的rect)。如果沒(méi)有設(shè)置則沒(méi)有動(dòng)畫(huà)效果
捕獲共享元素clip bounds,然后播放clip bounds變化動(dòng)畫(huà)。

/**
* ChangeClipBounds captures the {@link android.view.View#getClipBounds()} before and after the
* scene change and animates those changes during the transition.
*/
public class ChangeClipBounds extends Transition {
...
@Override
public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues,
...
Rect start = (Rect) startValues.values.get(PROPNAME_CLIP);
Rect end = (Rect) endValues.values.get(PROPNAME_CLIP);
boolean endIsNull = end == null;
...
endValues.view.setClipBounds(start);
RectEvaluator evaluator = new RectEvaluator(new Rect());
ObjectAnimator animator =
ObjectAnimator.ofObject(endValues.view, "clipBounds", evaluator, start, end);
if (endIsNull) {
final View endView = endValues.view;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
endView.setClipBounds(null);
}
});
}
return animator;
}
}
2.1.3 ChangeImageTransform :檢測(cè)ImageView(這里是專指ImageView)的尺寸,位置以及ScaleType,并創(chuàng)建相應(yīng)動(dòng)畫(huà)。
捕獲共享元素(ImageView)的transform matrices 屬性,然后播放ImageViewtransform matrices 屬性變化動(dòng)畫(huà)。與ChangeBounds相結(jié)合,這個(gè)變換可以讓ImageView在動(dòng)畫(huà)中高效實(shí)現(xiàn)大小,形狀或者ImageView.ScaleType
屬性平滑過(guò)度。
/**
* This Transition captures an ImageView's matrix before and after the
* scene change and animates it during the transition.
*
* <p>In combination with ChangeBounds, ChangeImageTransform allows ImageViews
* that change size, shape, or {@link android.widget.ImageView.ScaleType} to animate contents
* smoothly.</p>
*/
public class ChangeImageTransform extends Transition {
...
/**
* Creates an Animator for ImageViews moving, changing dimensions, and/or changing
* {@link android.widget.ImageView.ScaleType}.
*
* @param sceneRoot The root of the transition hierarchy.
* @param startValues The values for a specific target in the start scene.
* @param endValues The values for the target in the end scene.
* @return An Animator to move an ImageView or null if the View is not an ImageView,
* the Drawable changed, the View is not VISIBLE, or there was no change.
*/
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
if (startBounds == null || endBounds == null) {
return null;
}
Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
(startMatrix != null && startMatrix.equals(endMatrix));
if (startBounds.equals(endBounds) && matricesEqual) {
return null;
}
ImageView imageView = (ImageView) endValues.view;
Drawable drawable = imageView.getDrawable();
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
ObjectAnimator animator;
if (drawableWidth == 0 || drawableHeight == 0) {
animator = createNullAnimator(imageView);
} else {
if (startMatrix == null) {
startMatrix = Matrix.IDENTITY_MATRIX;
}
if (endMatrix == null) {
endMatrix = Matrix.IDENTITY_MATRIX;
}
ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
}
return animator;
}
...
}
2.1.4 ChangeScroll :滑動(dòng)的屬性發(fā)生了變化
/**
* This transition captures the scroll properties of targets before and after
* the scene change and animates any changes.
*/
public class ChangeScroll extends Transition {
...
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
final View view = endValues.view;
int startX = (Integer) startValues.values.get(PROPNAME_SCROLL_X);
int endX = (Integer) endValues.values.get(PROPNAME_SCROLL_X);
int startY = (Integer) startValues.values.get(PROPNAME_SCROLL_Y);
int endY = (Integer) endValues.values.get(PROPNAME_SCROLL_Y);
Animator scrollXAnimator = null;
Animator scrollYAnimator = null;
if (startX != endX) {
view.setScrollX(startX);
scrollXAnimator = ObjectAnimator.ofInt(view, "scrollX", startX, endX);
}
if (startY != endY) {
view.setScrollY(startY);
scrollYAnimator = ObjectAnimator.ofInt(view, "scrollY", startY, endY);
}
return TransitionUtils.mergeAnimators(scrollXAnimator, scrollYAnimator);
}
}
2.1.5 ChangeTransform :檢測(cè)view的scale和rotation創(chuàng)建縮放和旋轉(zhuǎn)動(dòng)畫(huà)
捕獲共享元素的縮放(scale)與旋轉(zhuǎn)(rotation)屬性 ,然后播放縮放(scale)與旋轉(zhuǎn)(rotation)屬性變化動(dòng)畫(huà)。

/**
* This Transition captures scale and rotation for Views before and after the
* scene change and animates those changes during the transition.
*
* A change in parent is handled as well by capturing the transforms from
* the parent before and after the scene change and animating those during the
* transition.
*/
public class ChangeTransform extends Transition {
...
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null ||
!startValues.values.containsKey(PROPNAME_PARENT) ||
!endValues.values.containsKey(PROPNAME_PARENT)) {
return null;
}
ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
boolean handleParentChange = mReparent && !parentsMatch(startParent, endParent);
Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_INTERMEDIATE_MATRIX);
if (startMatrix != null) {
startValues.values.put(PROPNAME_MATRIX, startMatrix);
}
Matrix startParentMatrix = (Matrix)
startValues.values.get(PROPNAME_INTERMEDIATE_PARENT_MATRIX);
if (startParentMatrix != null) {
startValues.values.put(PROPNAME_PARENT_MATRIX, startParentMatrix);
}
// First handle the parent change:
if (handleParentChange) {
setMatricesForParent(startValues, endValues);
}
// Next handle the normal matrix transform:
ObjectAnimator transformAnimator = createTransformAnimator(startValues, endValues,
handleParentChange);
if (handleParentChange && transformAnimator != null && mUseOverlay) {
createGhostView(sceneRoot, startValues, endValues);
}
return transformAnimator;
}
...
/**
* PathAnimatorMatrix allows the translations and the rest of the matrix to be set
* separately. This allows the PathMotion to affect the translations while scale
* and rotation are evaluated separately.
*/
private static class PathAnimatorMatrix {
...
}
}
2.2 如何使用Transition
2.2.1 TransitionManager.go(Scene scene, Transition transition)
直接在切換Scene時(shí),設(shè)置效果
2.2.2 根據(jù)設(shè)置的 transition文件 自動(dòng)生成Animator效果
在XML中或者在代碼中設(shè)置,舉個(gè)例子就是: res->transition文件下創(chuàng)建transition文件
//xxx.xml Fade、Slide、Explode :漸入、滑動(dòng)、爆炸
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
<explode/>
<fade/>
</transitionSet>
//或者直接new Fade()等等

設(shè)置 transition文件 自動(dòng)生成Animator效果,通常在兩種情況下使用:
- 切換Activity/Fragment時(shí)設(shè)置場(chǎng)景切換效果(這個(gè)部分在后續(xù)的文章會(huì)講到)
- beginDelayedTransition()設(shè)置延時(shí)動(dòng)畫(huà)
beginDelayedTransition
為每一個(gè)結(jié)束關(guān)鍵幀都專門(mén)設(shè)置一個(gè)xml的Scene布局豈不是很麻煩?如圖所示,點(diǎn)擊每個(gè)圖片的切換效果都差不多,那是不是就要設(shè)置4個(gè)Scene,然后對(duì)應(yīng)的點(diǎn)擊某個(gè)View就跳轉(zhuǎn)到某個(gè)Scene呢?

所以,就有了 :
延時(shí)動(dòng)畫(huà) :beginDelayedTransition()
例子:(四個(gè)ImageView,點(diǎn)擊每一個(gè),那個(gè)就會(huì)放大,其他三個(gè)就會(huì)消失)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootView"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView1"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginStart="72dp"
android:layout_marginTop="71dp"
app:srcCompat="@drawable/bangtang"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="120dp"
android:layout_height="120dp"
app:srcCompat="@drawable/shengdanshu"
android:layout_alignTop="@+id/imageView1"
android:layout_alignParentEnd="true"
android:layout_marginEnd="30dp" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="120dp"
android:layout_height="120dp"
app:srcCompat="@drawable/xueren"
android:layout_alignTop="@+id/imageView4"
android:layout_alignStart="@+id/imageView1" />
<ImageView
android:id="@+id/imageView4"
android:layout_width="120dp"
android:layout_height="120dp"
app:srcCompat="@drawable/xunlu"
android:layout_marginTop="24dp"
android:layout_below="@+id/imageView2"
android:layout_alignStart="@+id/imageView2" />
</RelativeLayout>
// explode_fade_changebounds.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds/>
<explode/>
<fade/>
</transitionSet>
public class BeginDelayedActivity extends AppCompatActivity implements View.OnClickListener{
ImageView iv1,iv2,iv3,iv4;
ViewGroup rootView;
boolean isBig=false;
int primarySize;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_begin_delayed);
rootView=findViewById(R.id.rootView);
iv1=findViewById(R.id.imageView1);
iv2=findViewById(R.id.imageView2);
iv3=findViewById(R.id.imageView3);
iv4=findViewById(R.id.imageView4);
primarySize=iv1.getLayoutParams().width;
iv1.setOnClickListener(this);
iv2.setOnClickListener(this);
iv3.setOnClickListener(this);
iv4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
//start scene 是當(dāng)前的scene
TransitionManager.beginDelayedTransition(rootView, TransitionInflater.from(this).inflateTransition(R.transition.explode_fade_changebounds));
//next scene 此時(shí)通過(guò)代碼已改變了scene statue
changeScene(v);
}
private void changeScene(View v) {
changeSize(v);
changeVisibility(iv1,iv2,iv3,iv4);
v.setVisibility(View.VISIBLE);
}
private void changeSize(View v) {
isBig=!isBig;
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
if(isBig){
layoutParams.width=(int)(1.5*primarySize);
layoutParams.height=(int)(1.5*primarySize);
}else {
layoutParams.width=primarySize;
layoutParams.height=primarySize;
}
v.setLayoutParams(layoutParams);
}
/**
* VISIBLE和INVISIBLE狀態(tài)切換
* @param ivs
*/
private void changeVisibility(ImageView ... ivs) {
for (View view:ivs){
view.setVisibility(view.getVisibility()==View.VISIBLE?View.INVISIBLE:View.VISIBLE);
}
}
}
我們?cè)谶@做詳細(xì)的分析 :
假設(shè)最開(kāi)始每個(gè)view都是可見(jiàn)的:
- 當(dāng)點(diǎn)擊事件發(fā)生之后調(diào)用TransitionManager的beginDelayedTransition()方法,并且傳遞了mRootView和一個(gè)Fade對(duì)象最為參數(shù)。之后,framework會(huì)立即調(diào)用transition類的captureStartValues()方法為每個(gè)view保存其當(dāng)前的可見(jiàn)狀態(tài)(visibility)。
- 當(dāng)beginDelayedTransition返回之后,在上面的代碼中將每個(gè)view設(shè)置為不可見(jiàn)。
- 在接下來(lái)的顯示中framework會(huì)調(diào)用transition類的captureEndValues()方法,記錄每個(gè)view最新的可見(jiàn)狀態(tài)。
- 接著,framework調(diào)用transition的createAnimator()方法。transition會(huì)分析每個(gè)view的開(kāi)始和結(jié)束時(shí)的數(shù)據(jù)發(fā)現(xiàn)view在開(kāi)始時(shí)是可見(jiàn)的,結(jié)束時(shí)是不可見(jiàn)的。Fade(transition的子類)會(huì)利用這些信息創(chuàng)建一個(gè)用于把view的alpha屬性變?yōu)?的AnimatorSet,并且將此AnimatorSet對(duì)象返回。
- framework會(huì)運(yùn)行返回的Animator,導(dǎo)致所有的View都漸漸消失。
這樣就達(dá)到了:通過(guò)屬性的改變,就發(fā)生動(dòng)畫(huà)...達(dá)到了代碼的精簡(jiǎn)
3. 總結(jié)
- 創(chuàng)建兩個(gè)Scene(起始關(guān)鍵幀 和 結(jié)束關(guān)鍵幀)
- 利用系統(tǒng)內(nèi)置的或自定義的transitions創(chuàng)建Animator
- 開(kāi)啟動(dòng)畫(huà)
這樣就是實(shí)現(xiàn)了簡(jiǎn)單的一個(gè)動(dòng)畫(huà)效果,這個(gè)過(guò)程我們只關(guān)心 開(kāi)始狀態(tài)和結(jié)束狀態(tài),并為狀態(tài)的變化規(guī)定了變化規(guī)律(transitions),然后自動(dòng)幫我們生成效果

由此可見(jiàn) :transition框架的兩個(gè)主要優(yōu)點(diǎn)
第一、Transitions抽象和封裝了屬性動(dòng)畫(huà),Animator的概念對(duì)開(kāi)發(fā)者來(lái)說(shuō)是透明的,因此它極大的精簡(jiǎn)了代碼量。開(kāi)發(fā)者所做的所有事情只是改變一下view前后的狀態(tài)數(shù)據(jù),Transition就會(huì)自動(dòng)的根據(jù)狀態(tài)的區(qū)別去生成動(dòng)畫(huà)效果。第二、不同場(chǎng)景之間變換的動(dòng)畫(huà)效果可以簡(jiǎn)單的通過(guò)使用不同的Transition類來(lái)改變
基礎(chǔ)的介紹就先講到這里!!!
上面講到了基礎(chǔ)的 TransitionManager.go() 和 beginDelayedTransition() 開(kāi)啟動(dòng)畫(huà)。其實(shí)還有一種開(kāi)啟方式更為常見(jiàn) :setEnterTransition()/setSharedElementEnterTransition() //當(dāng)然,這得看下回分解
Transition系列文章
一、初識(shí)Transition—實(shí)現(xiàn)兩個(gè)場(chǎng)景的變換
二、番外篇 Transition之ViewOverlay
三、定義 界面指定元素 或界面間共享元素 的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)基礎(chǔ)
四、Content Transition實(shí)現(xiàn)非共享元素轉(zhuǎn)場(chǎng)
五、SharedElementTransition之Activity間的轉(zhuǎn)場(chǎng)
六、SharedElementTransition之Fragment間的轉(zhuǎn)場(chǎng)
七、番外篇- 自定義Visibility
八、5.0以下實(shí)現(xiàn)共享轉(zhuǎn)場(chǎng)
本篇參考:
Activity和Fragment Transition介紹
Android 過(guò)渡(Transition)動(dòng)畫(huà)解析之基礎(chǔ)篇
animatedTransitionsLearn-master