Android轉場動畫
Android轉場動畫
課程目標:
1. 掌握定義轉場動畫的方法
學習內容:
1. 轉場動畫的作用
2. 哪些時機需要轉場動畫
3. 轉動畫的方法
一、 轉場動畫的作用
1. 轉場動畫可以提供視覺連續性。
二、 哪些時機需要轉場動畫
1. 視覺狀態改變時。
a. 單個檢視變化時
b. 佈局變化時
c. activity跳轉時
三、 轉場動畫的方法
1. 揭露效果(單個檢視的轉場動畫)
a. View狀態改變時(下面用一個demo演示當一個View在可見和不可見時產生揭露效果)
實踐:
xml佈局檔案: activity_reveal.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_view" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.imooc.transitionanimation.reveal.RevealActivity"> <CheckBox android:text="Play animation" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/checkBox" /> <View android:id="@+id/view" android:layout_centerInParent="true" android:background="@color/colorAccent" android:onClick="onClick" android:visibility="invisible" android:layout_width="300dp" android:layout_height="300dp"/> <Button android:onClick="onClick" android:id="@+id/buttonChangeVisibility" android:layout_width="wrap_content" android:text="switch" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" style="?android:buttonBarButtonStyle" android:layout_height="wrap_content" /> </RelativeLayout>
java程式碼: RecealActivity.java
public class RevealActivity extends AppCompatActivity { private static final String TAG = "RevealActivity"; private View mView; private CheckBox mPlayAnimationCheckBox; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reveal); mView = findViewById(R.id.view); mPlayAnimationCheckBox = (CheckBox) findViewById(R.id.checkBox); } public void onClick(View view) { final boolean playAnimation = mPlayAnimationCheckBox.isChecked(); switch (view.getId()) { case R.id.buttonChangeVisibility: handleChangeVisibility(playAnimation); break; } } private void handleChangeVisibility(boolean playAnimation) { Log.d(TAG, "handleChangeVisibility() called with: playAnimation = [" + playAnimation + "]"); Log.d(TAG, "handleChangeVisibility: " + mView.isShown()); if (playAnimation) { // 有揭露效果時View的變化效果 if (mView.isShown()) { revealExit(); } else { revealEnter(); } } else { // 沒有揭露效果時View的變化效果 if (mView.isShown()) { mView.setVisibility(View.INVISIBLE); } else { mView.setVisibility(View.VISIBLE); } } } // 通過揭露效果顯示View private void revealEnter() { int w = mView.getWidth(); int h = mView.getHeight(); int cx = w; int cy = h; int r = (int) Math.hypot(w, h); // 1. 建立Animator 物件(mView: 顯示揭露效果的物件 cx,cy: 產生介面效果的圓心(也就是起始點) 0: 動畫開始前半徑為0 r: 動畫結束的半徑) Animator animator = ViewAnimationUtils.createCircularReveal(mView, cx, cy, 0, r); mView.setVisibility(View.VISIBLE); animator.start(); } // 通過揭露效果隱藏View private void revealExit() { int w = mView.getWidth(); int h = mView.getHeight(); int cx = w; int cy = h; int r = (int) Math.hypot(w, h); // 1. 建立Animator 物件(mView: 顯示揭露效果的物件 cx,cy: 產生介面效果的圓心(也就是起始點) r: 動畫開始前半徑為0 0: 動畫結束的半徑) Animator animator = ViewAnimationUtils.createCircularReveal(mView, cx, cy, r, 0); animator.setDuration(5000); // 給Animator物件註冊監聽器,讓其在動畫結束時,View不可見 animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mView.setVisibility(View.INVISIBLE); } }); animator.start(); } }
2.多檢視的轉場動畫
為了完成多檢視的轉場動畫,Android中為我們提供了android.transition包。在這個包下
主要提供三個類:
1. Scene
2. Transition
3. TransitionManager
上面這三個類之間的關係: Scene就是場景,Transition就是轉換型別,TransitionManager負責場景的轉換,並將不同的轉換型別結合到場景轉換的過程中。
下面分別來看下這三個類:
1. Scene(場景)
我們螢幕上顯示的實際上就是一個View樹結構,我們看到的檢視其實就是這個View樹結構中所有View所處於的某種狀態的瞬間,這就是一個場景(Scene)。也就是說我們可以把檢視的某一個狀態稱為一個場景。
獲取這個場景的scene物件(從佈局中載入一個場景):
scene.getSceneForLayout(sceneRoot, R.layout.scene_overview, this);
引數含義:
sceneRoot:轉場發生的地方
R.layout.scene_overview:組成這一場景的佈局資源
this:上下文
2. Transition(轉換型別)
Android中為我們預定好了一些轉換效果,比如:
Fade(淡入淡出):也就是透明度變化
ChangeBounds(改變邊界)
AutoTransition(預設自動使用這個效果)
獲取Transition物件:
TransitionInflater.from(getBaseContext()).inflateTransition(R.transition.transition);
transition和我們之前的檢視物件中Animation以及屬性動畫中的Animator類似,他們都可以通過xml檔案和java程式碼定義,推薦使用xml檔案。
3. TransitionManager
有了場景物件和轉換型別,接著就可以進行轉場動畫了:
TransitionManager.go(mTargetScene, transition);
引數含義:
mTargetScene: 我們要跳轉的目標場景
transition: 使用的轉換型別,可以使我們自定義的,也可以用Android中提供的,如果不設定這個引數,預設使用AutoTransition這個場景。
實踐:
1.使用xml檔案自定義一個Transition物件,在res目錄下建立一個目錄/res/transition/, 然後建立一個xml檔案。(/res/transition/transition.xml):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform android:duration="3000">
<targets android:targetId="@id/image" />
</changeImageTransform>
<fade android:duration="3000" android:startDelay="1000">
</fade>
</transitionSet>
activity佈局檔案( activity_scene.xml ):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.scene.SceneActivity">
<FrameLayout
android:id="@+id/scene_root"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
進入場景xml佈局檔案( scene_overview.xml ):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.imooc.transitionanimation.scene.SceneActivity">
<ImageView
android:layout_width="match_parent"
android:id="@+id/image"
android:layout_height="0dp"
android:layout_weight="1"
android:src="@drawable/chang_bai"
android:transitionName="@string/pic"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<ImageButton
android:id="@+id/btnInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="4dp"
android:background="?android:selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/ic_info_black_24dp" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/btnInfo"
android:text="@string/title"
android:textAppearance="@style/TextAppearance.AppCompat.Headline" />
<TextView
android:id="@+id/tvAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvTitle"
android:layout_marginTop="8dp"
android:text="@string/address"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</RelativeLayout>
</LinearLayout>
退出場景xml佈局檔案( scene_info.xml ):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scene"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.imooc.transitionanimation.scene.SceneActivity">
<ImageView
android:id="@+id/image"
android:layout_width="160dp"
android:layout_height="120dp"
android:src="@drawable/chang_bai" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<ImageButton
android:id="@+id/btnClose"
android:transitionName="@string/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="4dp"
android:background="?android:selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/ic_close_black_24dp" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/btnClose"
android:text="@string/title"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:id="@+id/tvAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvTitle"
android:layout_marginTop="8dp"
android:text="@string/address"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<TextView
android:id="@+id/tvInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tvAddress"
android:layout_marginTop="8dp"
android:text="@string/info" />
</RelativeLayout>
</LinearLayout>
2.java程式碼:
public class SceneActivity extends AppCompatActivity {
private Scene mOverViewScene;
private Scene mInfoScene;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scene);
ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
// 獲取進入時的場景物件
mOverViewScene = Scene.getSceneForLayout(sceneRoot, R.layout.scene_overview, getBaseContext());
// 獲取退出時的場景物件
mInfoScene = Scene.getSceneForLayout(sceneRoot, R.layout.scene_info, getBaseContext());
// 第一次載入佈局時使用的場景轉換,如果沒有Transition引數,預設使用AutoTransition轉換型別
TransitionManager.go(mOverViewScene);
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.btnInfo:
// 自定義場景轉換型別transition
Transition transition = TransitionInflater.from(getBaseContext())
.inflateTransition(R.transition.transition);
TransitionManager.go(mInfoScene, transition);
break;
case R.id.btnClose:
TransitionManager.go(mOverViewScene);
break;
}
}
}
3.Activity間轉場動畫:
ActivityA和ActivityB之間跳轉的動畫涉及到3次動畫:
ActivityA跳轉到ActivityB時,ActivityA就會有離場動畫(Exit),進入到ActivityB時ActivityB就有進場動畫(Enter),當ActivityB按Back鍵返回到ActivityA時,ActivityA就又被再次進入,此時又是一種動畫效果(Re_enter)。
使用轉場動畫: (ActivityOptions)
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this);
startActivity(intent, options.toBundle());
轉場動畫的動畫型別: (Transition)
Android系統中預設的有三種:
1. Fade(淡入淡出)
2. Slide(滑動效果)
3. Explode(爆炸效果)
實踐:
a. xml佈局檔案:
activity_first.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_first"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.activity.FirstActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv1"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic1" />
<ImageView
android:id="@+id/iv2"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic2" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv3"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic3" />
<ImageView
android:id="@+id/iv4"
android:layout_width="0dp"
android:layout_height="160dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:onClick="onClick"
android:src="@drawable/pic4" />
</LinearLayout>
</LinearLayout>
activity_second.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_second"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.imooc.transitionanimation.activity.SecondActivity">
<ImageView
android:id="@+id/iv"
<!-- 共享元素定義的名稱,要和java程式碼中相同 -->
android:transitionName="img"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
b. java程式碼:
FirstActivity.java:
public class FirstActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
}
public void onClick(View view) {
int resId = -1;
switch (view.getId()) {
case R.id.iv1:
resId = R.drawable.pic1;
break;
case R.id.iv2:
resId = R.drawable.pic2;
break;
case R.id.iv3:
resId = R.drawable.pic3;
break;
case R.id.iv4:
resId = R.drawable.pic4;
break;
}
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("resId", resId);
// 定義動畫的型別(爆炸效果)
Transition transition = new Explode();
// 去除不想實現動畫效果的view(這裡設定的是狀態列不參加動畫效果)
transition.excludeTarget(android.R.id.statusBarBackground, true);
// 設定進場動畫
getWindow().setEnterTransition(transition);
// 設定離場動畫
getWindow().setExitTransition(transition);
// 設定再次進入時的動畫
getWindow().setReenterTransition(transition);
// 為共享元素設定特定的動畫效果
getWindow().setSharedElementEnterTransition(transition);
// 定義共享元素(注意這裡的名稱"img"要和xml佈局檔案中的view transitionName屬性名稱一樣)
Pair<View, String> shareElement = Pair.create(view, "img");
// 建立實現轉場動畫需要的options物件
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, shareElement);
startActivity(intent, options.toBundle());
}
}
SecondActivity.java:
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
int resId = getIntent().getExtras().getInt("resId");
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setTransitionName("img");
iv.setImageResource(resId);
// 下面這些含義解釋如上
Transition transition = new Explode();
transition.excludeTarget(android.R.id.statusBarBackground, true);
getWindow().setEnterTransition(transition);
getWindow().setExitTransition(transition);
}
}