- Android 5.0 以上使用 Transition 實現(xiàn)的方法
- Android 5.0 以下的實現(xiàn)方法
Transition
Transition 框架是 Android 4.4 KitKat 中加入的,但在 5.0 才開始被人應(yīng)用起來,
而且這一部分也涉及了 22.0 的 API,雖然有對應(yīng)的 support.v4 包,但也還是有點問題。
所以這一部分可以說是 5.0 以上適用的方法。
效果(錄制出來的效果有點卡頓):

- 設(shè)置 Activity 引用的 theme
設(shè)置windowContentTransitions為 true,即開啟窗口內(nèi)容過渡
<style name="AppTheme.custom">
<item name="colorControlHighlight">@color/ControlHighlight</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentTransitions">true</item>
</style>
這里遇到一點小問題,即上述 Activity 引用的 style 中不僅設(shè)置了 android:windowIsTranslucent,也設(shè)置了 android:windowIsTranslucent : 讓 Activity 的背景為透明,在我測試的時候發(fā)現(xiàn)使用共享元素的時候出現(xiàn)了返回時閃屏的現(xiàn)象,解決方法是設(shè)置 Activity 背景顏色為透明。
在 onCreate 中:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().getWindow().setBackgroundDrawableResource(R.color.transparent);
}
或者在上述的 style 中的 theme 添加:
<item name="android:windowBackground">@android:color/transparent</item>
- 設(shè)置共享元素
其實實現(xiàn)這樣的效果就是把第一個界面的 ImageView 移動、放大到第二個界面的 ImageView 的位置,借助 API 實現(xiàn)效果可以省去自己寫動畫的邏輯,但就需要讓系統(tǒng)關(guān)聯(lián)兩個 View。
而關(guān)聯(lián)兩個 View 通過設(shè)置android:transitionName屬性。
首先在第一個界面的activity_main.xml:
<LinearLayout>
<ImageView
android:id="@+id/img"
android:transitionName="testImg"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="160dp" />
...
</LinearLayout>
在打開的 Activity 的 xml 中
<LinearLayout>
<ImageView
android:id="@+id/item_img"
android:transitionName="testImg"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="380dp" />
...
</LinearLayout>
對應(yīng)的 ImageView 中的android:transitionName屬性值必須相同,而對兩個控件的大小、id 等屬性并無要求。
- 第一個 Activity 中,使用共享元素啟動新界面方法
MainActivity:
Intent intent = new Intent(getActivity(),SecondActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(getActivity(),
mImgView,"testImg");
startActivity(intent,options.toBundle());
在 makeSceneTransitionAnimation 傳入的參數(shù)中,mImgView 是第一個界面中 ImageView 的實例,第三個參數(shù)對應(yīng) xml 中的 android:transitionName 的值。
- 在被打開的 Activity 中
首先加載圖片還是要自己寫的,其余的需要注意:
返回不再調(diào)用finishActivity()而是supportFinishAfterTransition()。
@Override
public void onBackPressed() {
supportFinishAfterTransition();
}
因為打開新的 Activity 的時候,可能要去加載新的圖片,這時候我們需要延遲過渡動畫的開始,直到圖片加載完成之后再開始動畫。否則會出現(xiàn)各種 bug。
所以要在第二個 Activity 中的 onCreate() 中阻止動畫的執(zhí)行:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 延遲共享動畫的執(zhí)行
postponeEnterTransition();
}
然后在圖片加載完成后開始動畫:
Glide.with(this)
.load(data.getImage())
.priority(Priority.HIGH)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(new GlideDrawableImageViewTarget(mImageView){
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
super.onResourceReady(resource, animation);
//圖片加載完成的回調(diào)中,啟動過渡動畫
supportStartPostponedEnterTransition();
}
});
當然,啟動動畫不一定要等待圖片加載完成再進行,因為還存在著圖片加載失敗、加載時間過長等問題,這里只是提出一種方法,實際還是自己看情況決定。
以上只是簡單的實現(xiàn)了一種效果,關(guān)于 Transition 的使用、共享元素在
Fragment 中的使用、多個共享元素的使用等,在這里暫時不打算細講,可以參考:
使用 Transition FrameWork 實現(xiàn)有意義的轉(zhuǎn)場動畫(譯)
(譯)Android 5.0 頁面共享元素過渡
定義定制動畫
利用動畫效果實現(xiàn)
- 效果:

- 設(shè)置 Activity 背景透明
<style name="AppTheme.custom">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
- 創(chuàng)建一個實體類以存儲 ImageView 的位置數(shù)據(jù)并實現(xiàn) Parcelable 接口,
位置數(shù)據(jù)包括控件距離父控件( 屏幕 )左邊、頂部的距離和控件的寬高。
public static class ImageBean implements Parcelable {
private String FilePath;
private String FileName;
private Boolean IsSelect;
private int viewLeft;
private int viewTop;
private int viewHeight;
private int viewWidth;
public int getViewLeft() {
return viewLeft;
}
public void setViewLeft(int viewLeft) {
this.viewLeft = viewLeft;
}
public int getViewTop() {
return viewTop;
}
public void setViewTop(int viewTop) {
this.viewTop = viewTop;
}
public int getViewHeight() {
return viewHeight;
}
public void setViewHeight(int viewHeight) {
this.viewHeight = viewHeight;
}
public int getViewWidth() {
return viewWidth;
}
public void setViewWidth(int viewWidth) {
this.viewWidth = viewWidth;
}
public ImageBean(int left,int top,int height,int width){
viewLeft = left;
viewTop = top;
viewHeight = height;
viewWidth = width;
}
public ImageBean(String p, String f){
FilePath = p;
FileName = f;
IsSelect = false;
}
public String getFilePath() {
return FilePath;
}
public void setFilePath(String filePath) {
FilePath = filePath;
}
public String getFileName() {
return FileName;
}
public void setFileName(String fileName) {
FileName = fileName;
}
public Boolean getSelect() {
return IsSelect;
}
public void setSelect(Boolean select) {
IsSelect = select;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.FilePath);
dest.writeString(this.FileName);
dest.writeValue(this.IsSelect);
dest.writeInt(this.viewLeft);
dest.writeInt(this.viewTop);
dest.writeInt(this.viewHeight);
dest.writeInt(this.viewWidth);
}
protected ImageBean(Parcel in) {
this.FilePath = in.readString();
this.FileName = in.readString();
this.IsSelect = (Boolean) in.readValue(Boolean.class.getClassLoader());
this.viewLeft = in.readInt();
this.viewTop = in.readInt();
this.viewHeight = in.readInt();
this.viewWidth = in.readInt();
}
public static final Parcelable.Creator<ImageBean> CREATOR = new Parcelable.Creator<ImageBean>() {
@Override
public ImageBean createFromParcel(Parcel source) {
return new ImageBean(source);
}
@Override
public ImageBean[] newArray(int size) {
return new ImageBean[size];
}
};
}
- 在打開新的 Activity 前,獲取點擊的 ImageView 的位置數(shù)據(jù)
private ImageBean img2Location(ImageView imageView,String path){
int[] location = new int[2];
imageView.getLocationOnScreen(location);
ImageBean bean = new ImageBean(
location[0],location[1],
imageView.getHeight(),imageView.getWidth());
bean.setFilePath(path);
return bean;
}
- 通過 Intent 將存有位置數(shù)據(jù)的 ImageBean 實例傳到新的 Activity
String path = "..";
ImageView imageView = ...;
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("location",img2Location(imageView,path));
startActivity(intent);
- 在打開的 Activity 中,通過傳過來的 path 數(shù)據(jù)加載圖片,
同時獲取傳過來的位置數(shù)據(jù)。
public void initView(@Nullable final View view) {
getActivity().setTheme(R.style.translucent);
Glide.with(getActivity()).load(bean.getFilePath())
.placeholder(R.drawable.black_place_holder).into(mPhotoView);
//獲取位置數(shù)據(jù)
Intent intent = getIntent();
ImageBean bean = intent.getParcelableExtra("image");
int mOriginLeft = bean.getViewLeft();
int mOriginTop = bean.getViewTop();
int mOriginHeight = bean.getViewHeight();
int mOriginWidth = bean.getViewWidth();
}
- 監(jiān)聽返回按鈕,點擊時開始移動、縮放的動畫,
動畫用 ValueAnimator 和 ScaleAnimation 實現(xiàn)。
private void finishPhotoViewWithTap(int left,int top){
//動畫的執(zhí)行時間
int ANIMATOR_DURATION = 500;
//控制 x 軸上的移動,將 ImageView 移動到對應(yīng)的位置
ValueAnimator translateXAnimator = ValueAnimator.ofFloat(0, left);
translateXAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//用 setX 移動控件
mPhotoView.setX((Float) valueAnimator.getAnimatedValue());
}
});
translateXAnimator.setDuration(ANIMATOR_DURATION);
translateXAnimator.start();
///控制 y 軸上的移動,并監(jiān)聽動畫的結(jié)束,動畫結(jié)束時關(guān)閉該 Activity
ValueAnimator translateYAnimator = ValueAnimator.ofFloat(0, top);
translateYAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mPhotoView.setY((Float) valueAnimator.getAnimatedValue());
}
});
translateYAnimator.setDuration(ANIMATOR_DURATION);
translateYAnimator.start();
translateXAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
//動畫結(jié)束的回調(diào)
animation.removeAllListeners();
finishActivity();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
//控制縮放的 Animation
Animation scaleAnimation = new ScaleAnimation(1.0f,0.2f,1.0f,0.2f
,left,top);
//控制透明度的 Animation
Animation alphaAnimation = new AlphaAnimation(1.0f,0f);
//用 AnimationSet 將以上兩個 Animation 結(jié)合到一起
AnimationSet set = new AnimationSet(true);
set.setDuration(ANIMATOR_DURATION);
set.setFillAfter(true);
set.addAnimation(scaleAnimation);
set.addAnimation(alphaAnimation);
mPhotoView.startAnimation(set);
}
原理大概就是如此,實現(xiàn)動畫的方法有很多種。
可以參考:
Activity 共享元素轉(zhuǎn)場動畫實踐
Android共享元素轉(zhuǎn)場動畫兼容實踐