Android中轉(zhuǎn)場(chǎng)動(dòng)畫的實(shí)現(xiàn)與兼容性處理
前言
在 Android 5.0 之前,我們已經(jīng)有了 overridePendingTransition() 方法來實(shí)現(xiàn)一些轉(zhuǎn)場(chǎng)效果。然而,在 Android 5.0 以后,轉(zhuǎn)場(chǎng)效果更加炫酷。
比如下面的動(dòng)畫:

一、Android L 中的轉(zhuǎn)場(chǎng)動(dòng)畫
實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫只需三步:
在 res/ 目錄下創(chuàng)建 transition 文件夾,在該文件夾下定義界面轉(zhuǎn)場(chǎng)動(dòng)畫和共享元素的動(dòng)畫。
在 res/value/style 文件中為每個(gè) Activity 指定轉(zhuǎn)場(chǎng)動(dòng)畫的 style ,并在 AndroidManifest.xml 文件中為每個(gè) Activity 設(shè)置對(duì)應(yīng)的 android:theme。
在 Activity 調(diào)用 startActivity() 切換動(dòng)畫前,使用 ActivityOptionsCompat 來創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫時(shí)的共享對(duì)象。
下面就來對(duì)這三步進(jìn)行詳細(xì)講解。
二、定義轉(zhuǎn)場(chǎng)動(dòng)畫
在 res/ 目錄下創(chuàng)建了 transition 資源文件夾后,就可以在該文件夾下對(duì)每一種動(dòng)畫進(jìn)行定義。
一般來說,對(duì) Activity 定義一個(gè)過渡動(dòng)畫可以寫成下面的形式:
<explode xmlns:android="http://schemas.android.com/apk/res/android"> <targets> <target android:excludeId="@android:id/statusBarBackground"/> <target android:excludeId="@android:id/navigationBarBackground"/> </targets> </explode>
其中, 是動(dòng)畫效果的名稱,Android 5.0(API 級(jí)別 21)支持這些進(jìn)入與退出轉(zhuǎn)換:
1、分解(explode):從場(chǎng)景中心移入或移出視圖。
2、滑動(dòng)(slide):從場(chǎng)景邊緣移入或移出視圖。
3、淡入淡出(fade):通過調(diào)整透明度在場(chǎng)景中增添或移除視圖。
而每一種動(dòng)畫效果,都有額外的屬性。比如滑動(dòng) slide,可以使用 android:slideEdge="top" 設(shè)置滑動(dòng)的方向;淡入淡出(fade)可以使用 android:fadingMode="fade_in" 設(shè)置具體是淡入(fade_in)還是淡出(fade_out)等。
標(biāo)簽里面定義需要轉(zhuǎn)場(chǎng)(或者不需要轉(zhuǎn)場(chǎng))的目標(biāo) id ,這個(gè) id 可以使系統(tǒng)自帶的,也可以是我們自己視圖中的 view 的 id,每一個(gè) id 需要單獨(dú)在 標(biāo)簽中定義,android:targetId 表示目標(biāo)ID需要進(jìn)行過渡轉(zhuǎn)換的 view,而 android:excludeId 表示我們不需要該 ID 的 view 進(jìn)行過渡轉(zhuǎn)場(chǎng)。上面的那段代碼的意思是說,除了狀態(tài)欄和導(dǎo)航欄以外所有的 view,都執(zhí)行 explode 動(dòng)畫。
如果我們想要在同一個(gè)過渡狀態(tài)中實(shí)現(xiàn)兩種或多種動(dòng)畫效果怎么辦?也簡(jiǎn)單,將根標(biāo)簽替換為 ,然后定義每一種動(dòng)畫效果,最后記得在根標(biāo)簽中使用 android:transitionOrdering 注明這幾種動(dòng)畫的演示順序,sequential 表示順序執(zhí)行,而 together 表示同時(shí)執(zhí)行。
比如像下面的代碼:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <slide android:slideEdge="bottom"> <targets> <target android:targetId="@id/cardview"/> </targets> </slide> <fade> <targets> <target android:excludeId="@android:id/statusBarBackground"/> <target android:excludeId="@android:id/navigationBarBackground"/> <target android:excludeId="@id/cardview"/> </targets> </fade> </transitionSet>
這段代碼的意思就很簡(jiǎn)單了,該 xml 定義了兩個(gè)過渡動(dòng)畫,并且同時(shí)執(zhí)行。第一個(gè)動(dòng)畫是針對(duì) id 為 cardView 的 view 進(jìn)行滑動(dòng),第二個(gè)動(dòng)畫將除了狀態(tài)欄、導(dǎo)航欄和 cardview 以外的 view,進(jìn)行淡入淡出。
三、為每個(gè) Activity 定義轉(zhuǎn)場(chǎng)樣式
這里的每一種動(dòng)畫,指的是在進(jìn)行界面跳轉(zhuǎn)過渡時(shí),兩個(gè)界面的狀態(tài)。比如對(duì)于 Activity A 和 Activity B 這兩個(gè)界面,可能的狀態(tài)如下:
1、界面 A 跳轉(zhuǎn)至界面 B :這時(shí)界面 A 是退出(exit )過渡狀態(tài),而對(duì)應(yīng)的界面B是進(jìn)入(enter)過渡狀態(tài)。
2、界面 B 返回到界面 A :這時(shí)界面 A 是重新進(jìn)入(reenter)過渡,而對(duì)應(yīng)的界面B則是返回(return)過渡。
一般來說,所有的 Activity 過渡動(dòng)畫都可以定義成如下的形式:
<style name="BaseAppTheme" parent="android:Theme.Material"> <!-- 開啟過渡效果 --> <item name="android:windowContentTransitions">true</item> <!-- 指定界面進(jìn)入/退出動(dòng)畫效果 --> <item name="android:windowEnterTransition">@transition/explode</item> <item name="android:windowExitTransition">@transition/explode</item> <!-- 指定共享元素進(jìn)入/退出的動(dòng)畫效果 --> <item name="android:windowSharedElementEnterTransition"> @transition/change_image_transform</item> <item name="android:windowSharedElementExitTransition"> @transition/change_image_transform</item> </style>
當(dāng)然,你可以不用寫全,比如在我的 Demo 中一個(gè)界面的轉(zhuǎn)場(chǎng)動(dòng)畫文件如下:
<style name="AppTheme.Detail"> <item name="windowActionBar">false</item> <item name="android:windowNoTitle">true</item> <item name="android:windowTranslucentStatus">true</item> <item name="android:windowAllowEnterTransitionOverlap">false</item> <item name="android:windowEnterTransition">@transition/detail_enter</item> </style>
四、調(diào)用 ActivityOptionsCompat
轉(zhuǎn)場(chǎng)動(dòng)畫是在兩個(gè)界面的跳轉(zhuǎn)返回時(shí)發(fā)生的,所以,當(dāng)使用 intent 跳轉(zhuǎn)界面時(shí),需要調(diào)用 ActivityOptionsCompat來指定動(dòng)畫的運(yùn)行。
一般來說,調(diào)用 ActivityOptionsCompat 的模板代碼如下:
// 創(chuàng)建一個(gè)包含過渡動(dòng)畫信息的 ActivityOptions 對(duì)象 ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, view, getString(R.string.image_transition_name)); // 使用 Intent 跳轉(zhuǎn)界面,并傳遞共享對(duì)象信息 Intent intent = new Intent(this, DetailActivity.class); startActivity(intent, optionsCompat.toBundle());
ActivityOptionsCompat 是在support v4 包里面的,其實(shí)它是 ActivityOptions 的一個(gè)兼容(ActivityOptions是API 16引入的)。
然后,我們需要在第二個(gè) Activity 中,將轉(zhuǎn)場(chǎng)的圖片獲取并顯示到界面中。
多個(gè)共享元素的過渡實(shí)現(xiàn)
有時(shí)候我們需要讓多個(gè)元素產(chǎn)生動(dòng)畫效果,可以使用 Pair<> 來實(shí)現(xiàn):
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view1, "agreedName1"), Pair.create(view2, "agreedName2"));
五、手動(dòng)實(shí)現(xiàn)一個(gè)轉(zhuǎn)場(chǎng)動(dòng)畫
現(xiàn)在市面上,Android 5.0 以下的手機(jī)系統(tǒng)還有一定的市場(chǎng)份額,所以為了照顧這些用戶,我們只能手動(dòng)實(shí)現(xiàn)一下共享元素的轉(zhuǎn)場(chǎng)動(dòng)畫效果。
實(shí)現(xiàn)的思路也比較簡(jiǎn)單,大概的步驟如下:
1、確定第一個(gè)界面的共享元素,將其信息傳遞個(gè)第二個(gè)界面
2、第二個(gè)界面接收信息,開始的時(shí)候?qū)⒔缑嬖O(shè)置為透明,并只顯示共享元素。
3、將第二個(gè)界面的共享元素進(jìn)行動(dòng)畫處理。
那么我們開始一步步實(shí)現(xiàn)上面的步驟。
獲取共享元素位置信息
在第一個(gè)界面中,我們需要獲取到共享元素的位置信息,并將其傳遞給下一個(gè)界面。于是乎,我們可以在第一個(gè)界面元素點(diǎn)擊事件中,這么寫:
public void imageClick(View view) {
Intent intent = new Intent(AnimeActivity.this, AnimeDetailActivity.class);
// 創(chuàng)建一個(gè) rect 對(duì)象來存儲(chǔ)共享元素位置信息
Rect rect = new Rect();
// 獲取元素位置信息
view.getGlobalVisibleRect(rect);
// 將位置信息附加到 intent 上
intent.setSourceBounds(rect);
CustomImage customImage = (CustomImage) view;
intent.putExtra(AnimeDetailActivity.EXTRA_IMAGE, customImage.getImageId());
startActivity(intent);
// 屏蔽 Activity 默認(rèn)轉(zhuǎn)場(chǎng)效果
overridePendingTransition(0, 0);
}
其中,getGlobalVisibleRect() 方法的含義是,獲取 可見的狀態(tài)欄高度+可見的標(biāo)題欄高度+Rect左上角到標(biāo)題欄底部的距離,如果標(biāo)題欄被隱藏了,那么可見標(biāo)題欄高度為0。
接下來,就在在第二個(gè)界面接收位置信息并將該圖片展示出來了。
模擬轉(zhuǎn)場(chǎng)動(dòng)畫
在第二個(gè)界面中,我們需要做如下的操作:
1、獲取上共享元素信息。
2、計(jì)算共享元素縮放比例和位移距離。
3、調(diào)用動(dòng)畫,完成模擬轉(zhuǎn)場(chǎng)效果。
我將上面三個(gè)步驟的代碼如下。
private void initial() {
// 獲取上一個(gè)界面?zhèn)魅氲男畔?
mRect = getIntent().getSourceBounds();
mRescourceId = getIntent().getExtras().getInt(EXTRA_IMAGE);
// 獲取上一個(gè)界面中,圖片的寬度和高度
mOriginWidth = mRect.right - mRect.left;
mOriginHeight = mRect.bottom - mRect.top;
// 設(shè)置 ImageView 的位置,使其和上一個(gè)界面中圖片的位置重合
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mOriginWidth, mOriginHeight);
params.setMargins(mRect.left, mRect.top - getStatusBarHeight(), mRect.right, mRect.bottom);
mImageView.setLayoutParams(params);
// 設(shè)置 ImageView 的圖片和縮放類型
mImageView.setImageResource(mRescourceId);
mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
// 根據(jù)上一個(gè)界面?zhèn)魅氲膱D片資源 ID,獲取圖片的 Bitmap 對(duì)象。
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(mRescourceId);
Bitmap bitmap = bitmapDrawable.getBitmap();
// 計(jì)算圖片縮放比例和位移距離
getBundleInfo(bitmap);
// 創(chuàng)建一個(gè) Pallette 對(duì)象
mImagePalette = Palette.from(bitmap).generate();
// 使用 Palette 設(shè)置背景顏色
mContainer.setBackgroundColor(
mImagePalette.getVibrantColor(ContextCompat.getColor(this, android.R.color.black)));
}
在12行,通過設(shè)置 Margin 的形式來確定圖片的位置,需要注意的是,由于狀態(tài)欄是在父控件 FramLayout 之外的,因此我們要將 Rect.top 的值減去狀態(tài)欄的高度,這樣才是相對(duì)于屏幕的絕對(duì)位置。然后,getBundleInfo() 方法的代碼如下:
private void getBundleInfo(Bitmap bitmap) {
// 計(jì)算圖片縮放比例,并存儲(chǔ)在 bundle 中
if (bitmap.getWidth() >= bitmap.getHeight()) {
mScaleBundle.putFloat(SCALE_WIDTH, (float) mScreenWidth / mOriginWidth);
mScaleBundle.putFloat(SCALE_HEIGHT, (float) bitmap.getHeight() / mOriginHeight);
} else {
mScaleBundle.putFloat(SCALE_WIDTH, (float) bitmap.getWidth() / mOriginWidth);
mScaleBundle.putFloat(SCALE_HEIGHT, (float) mScreenHeight / mOriginHeight);
}
// 計(jì)算位移距離,并將數(shù)據(jù)存儲(chǔ)到 bundle 中
mTransitionBundle.putFloat(TRANSITION_X, mScreenWidth / 2 - (mRect.left + (mRect.right - mRect.left) / 2));
mTransitionBundle.putFloat(TRANSITION_Y, mScreenHeight / 2 - (mRect.top + (mRect.bottom - mRect.top) / 2));
}
動(dòng)畫處理
最后我們需要使用動(dòng)畫來模擬轉(zhuǎn)場(chǎng)效果,代碼如下:
private void runEnterAnim() {
mImageView.animate()
.setInterpolator(DEFAULT_INTERPOLATOR)
.setDuration(DURATION)
.scaleX(mScaleBundle.getFloat(SCALE_WIDTH))
.scaleY(mScaleBundle.getFloat(SCALE_HEIGHT))
.translationX(mTransitionBundle.getFloat(TRANSITION_X))
.translationY(mTransitionBundle.getFloat(TRANSITION_Y))
.start();
}
很簡(jiǎn)單,自此,入場(chǎng)動(dòng)畫效果基本模擬完畢。
而退場(chǎng)動(dòng)畫就更簡(jiǎn)單了,直接上代碼:
private void runExitAnim() {
mImageView.animate()
.setInterpolator(DEFAULT_INTERPOLATOR)
.setDuration(DURATION)
.scaleX(1)
.scaleY(1)
.translationX(0)
.translationY(0)
.withEndAction(new Runnable() {
@Override
public void run() {
finish();
overridePendingTransition(0, 0);
}
})
.start();
}
總結(jié)
以上就是Android中實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫的全部?jī)?nèi)容,所以,是不是很簡(jiǎn)單?希望這篇文章的內(nèi)容對(duì)各位Android開發(fā)者們能有所幫助,如果有疑問大家可以留言交流。
- Android轉(zhuǎn)場(chǎng)動(dòng)畫深入分析探究
- Android?Flutter實(shí)現(xiàn)頁面切換轉(zhuǎn)場(chǎng)動(dòng)畫效果
- Android實(shí)現(xiàn)Reveal圓形Activity轉(zhuǎn)場(chǎng)動(dòng)畫的完整步驟
- Android工具欄頂出轉(zhuǎn)場(chǎng)動(dòng)畫的實(shí)現(xiàn)方法實(shí)例
- Android5.0之Activity的轉(zhuǎn)場(chǎng)動(dòng)畫的示例
- 詳解Android(共享元素)轉(zhuǎn)場(chǎng)動(dòng)畫開發(fā)實(shí)踐
- Android轉(zhuǎn)場(chǎng)效果實(shí)現(xiàn)示例淺析
相關(guān)文章
Android Studio真機(jī)無線連接USB設(shè)備調(diào)試運(yùn)行詳解流程
你在Android Studio寫app時(shí)是否也有想過如果可以不用數(shù)據(jù)線連接手機(jī)調(diào)試運(yùn)行就好了?如果需要取出數(shù)據(jù)線插接的話我肯定是嫌麻煩的,但是模擬器有時(shí)候需要測(cè)試一些需要硬件支持的功能時(shí)又不管用,所以最好的測(cè)試還是在真機(jī)上,本篇教你扔掉數(shù)據(jù)線來無線調(diào)試2021-11-11
Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android studio實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
Android仿微信錄音功能(錄音后的raw文件轉(zhuǎn)mp3文件)
這篇文章主要介紹了Android中仿微信錄音功能錄音后的raw文件轉(zhuǎn)mp3文件,本文通過實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下2019-11-11
Android SeekBar實(shí)現(xiàn)禁止滑動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android SeekBar實(shí)現(xiàn)禁止滑動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
Android使用Recyclerview實(shí)現(xiàn)圖片水平自動(dòng)循環(huán)滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android使用Recyclerview實(shí)現(xiàn)圖片水平自動(dòng)循環(huán)滾動(dòng)效果,實(shí)現(xiàn)精彩的跑馬燈效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android實(shí)現(xiàn)定時(shí)任務(wù)功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)定時(shí)任務(wù)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
Android開發(fā)進(jìn)階自定義控件之滑動(dòng)開關(guān)實(shí)現(xiàn)方法【附demo源碼下載】
這篇文章主要介紹了Android開發(fā)進(jìn)階自定義控件之滑動(dòng)開關(guān)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了Android自定義開關(guān)控件的原理、實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2016-08-08
Android動(dòng)態(tài)表格的實(shí)現(xiàn)代碼(內(nèi)容、樣式可擴(kuò)縮)
這篇文章主要介紹了Android動(dòng)態(tài)表格的實(shí)現(xiàn)代碼(內(nèi)容、樣式可擴(kuò)縮),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09

