編輯:關於Android編程
Android5.0之後為我們提供了許多炫酷的界面過渡效果,其中共享元素過渡也是很有亮點的一個效果,但這個效果只能在Android5.0之後使用,那今天我們就來將共享元素過渡效果兼容到Android4.0,讓5.0之前的手機也可以體驗這麼炫酷的效果吧。
A transition animation compatible Library.
兼容Android5.0之後轉場動畫至Android4.0。
github地址:https://github.com/zhangke3016/TranslationCompat
依慣例,首先來說下本文的行文思路吧:
一、頁面過渡兼容庫的使用
二、頁面過渡兼容庫實現原理淺析
三、用兼容庫將開源項目MaterialLogin動畫效果兼容至Android4.0
原項目地址:MaterialLogin將動畫效果兼容至Android4.0
使用這個兼容庫也很簡單,首先,在要控制跳轉的頁面調用TransitionController.getInstance().startActivity方法來實現跳轉,在其中主要是傳入當前界面要過渡到另一頁面的過渡元素View,以及另一個頁面對應共享元素的View id值。
然後,在跳轉到的第二個頁面調用TransitionController.getInstance().show方法來實現元素的過渡,傳入參數也很簡單。
最後呢,在頁面返回的時候,調用TransitionController.getInstance().exitActivity方法即可。
這樣一個完整的界面過渡動畫基本就可以使用了,當然,為了讓實現的效果更炫酷,加入了對過渡動畫狀態的監聽,可以在動畫結束時加入自己的操作,為方便起見,兼容庫包含圓形元素過渡:調用ViewAnimationCompatUtils.createCircularReveal方法既可實現元素以圓形展開和收起,使用方式和ViewAnimationUtils類一致,以及矩形元素過渡:調用:
ViewAnimationCompatUtils.createRectReveal方法既可實現元素以矩形方式以左、上、右、下四個方向展開。
具體代碼如下:
//參數一:當前Activity
//參數二:跳轉意圖
//參數三:當前頁面跳轉至下一頁面的View
//參數四:下一頁面關聯的View id
TransitionController.getInstance().startActivity(this,new Intent(this, RegisterActivity.class),fab,R.id.fab);
//跳轉後頁面調用:
TransitionController.getInstance().show(this,getIntent());
可在show方法調用之前設置監聽:
TransitionController.getInstance().setEnterListener(new TransitionCustomListener() {
@Override
public void onTransitionStart(Animator animator) {
}
@Override
public void onTransitionEnd(Animator animator) {
}
@Override
public void onTransitionCancel(Animator animator) {
}
});
//界面退出的時候調用
TransitionController.getInstance().exitActivity(PageDetailActivity.this);
//增加界面圓形轉換動畫
// 用法及參數和ViewAnimationUtils一致
ViewAnimationCompatUtils.createCircularReveal(cvAdd, cvAdd.getWidth()/2,0, fab.getWidth() / 2, cvAdd.getHeight());
//增加界面矩形轉換動畫
Animator mAnimator = ViewAnimationCompatUtils.createRectReveal( nsv, 0, nsv.getHeight(),ViewAnimationCompatUtils.RECT_TOP);
二、頁面過渡兼容庫實現原理淺析
先講了這個兼容庫的用法,現在來聊聊它是怎麼實現的,可以把主要實現細分六步:
1、獲取跳轉頁面過渡元素的位置
2、將跳轉過渡元素的位置傳給下一個頁面
3、在跳轉到的頁面獲取位置信息並創建相同寬高大小的元素和其覆蓋屏幕的父容器,並將新創建的元素添加到父容器中,而父容器添加至根視圖中
4、獲取跳轉到的頁面元素截圖並將其設為創建元素的背景
5、將當前新元素位置與跳轉到頁面對比獲取縮放比例與移動距離並開始動畫,結束後將父容器隱藏
6、界面返回時將創建的父容器重新添加至下一個頁面動畫實現,將創建的元素以動畫形式返回初始位置,結束後移除父容器
1、獲取跳轉頁面過渡元素的位置
//rect 來存儲共享元素位置信息
Rect rect = new Rect();
// 獲取元素位置信息
view.getGlobalVisibleRect(rect);
2、將跳轉過渡元素的位置傳給下一個頁面
// 將位置信息附加到 intent 上
intent.setSourceBounds(rect);
intent.putExtra(TRANSITION_NEXT_ID, nextShowViewId);
3、在跳轉到的頁面獲取位置信息並創建相同寬高大小的元素和其覆蓋屏幕的父容器,並將新創建的元素添加到父容器中,而父容器添加至根視圖中
View virtalView = new View(activity);
Bitmap cacheBitmap = BitmapUtil.getCacheBitmapFromView(next_view);
// 獲取上一個界面中,元素的寬度和高度
final int mOriginWidth = mRect.right - mRect.left;
final int mOriginHeight = mRect.bottom - mRect.top;
getBundleInfo(next_view,mOriginWidth,mOriginHeight,mRect);
//創建覆蓋屏幕的父容器
mContainer = new FrameLayout(activity);
FrameLayout.LayoutParams mContainerParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//父容器添加至根視圖中
parent.addView(mContainer,mContainerParams);
if (mBgColor!=-1)
mContainer.setBackgroundColor(ContextCompat.getColor(activity, mBgColor));
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mOriginWidth, mOriginHeight);
params.setMargins(mRect.left, mRect.top - BarUtils.getActionBarHeight(activity) -getStatusBarHeight(activity), mRect.right, mRect.bottom);
virtalView.setBackgroundDrawable(new BitmapDrawable(activity.getResources(), cacheBitmap));
//創建相同寬高大小的元素
virtalView.setLayoutParams(params);
//將新創建的元素添加到父容器中
mContainer.addView(virtalView);
4、獲取跳轉到的頁面元素截圖並將其設為創建元素的背景
//獲取跳轉到的頁面元素截圖
Bitmap cacheBitmap = BitmapUtil.getCacheBitmapFromView(next_view);
//將其設為創建元素的背景
virtalView.setBackgroundDrawable(new BitmapDrawable(activity.getResources(), cacheBitmap));
/**
* 獲取一個 View 的緩存視圖
*
* @param view
* @return
*/
public static Bitmap getCacheBitmapFromView(View view) {
final boolean drawingCacheEnabled = true;
view.setDrawingCacheEnabled(drawingCacheEnabled);
view.buildDrawingCache(drawingCacheEnabled);
final Bitmap drawingCache = view.getDrawingCache();
Bitmap bitmap;
if (drawingCache != null) {
bitmap = Bitmap.createBitmap(drawingCache);
view.setDrawingCacheEnabled(false);
} else {
bitmap = null;
}
return bitmap;
}
5、將當前新元素位置與跳轉到頁面對比獲取縮放比例與移動距離並開始動畫,結束後將父容器隱藏
//將當前新元素位置與跳轉到頁面對比獲取縮放比例與移動距離
getBundleInfo(next_view,mOriginWidth,mOriginHeight,mRect);
//開始動畫
runEnterAnim(virtalView,next_view,mContainer);
/**
* 計算縮放比例,以及位移距離
*
* @param
*/
private void getBundleInfo(View mView,int mOriginWidth,int mOriginHeight,Rect mRect) {
// 計算縮放比例
mScaleBundle.putFloat(SCALE_WIDTH, (float) mView.getWidth() / mOriginWidth);
mScaleBundle.putFloat(SCALE_HEIGHT, (float) mView.getHeight() / mOriginHeight);
Rect rect = new Rect();
mView.getGlobalVisibleRect(rect);
// 計算位移距離
mTransitionBundle.putFloat(TRANSITION_X, (rect.left+(rect.right - rect.left) / 2) - (mRect.left + (mRect.right - mRect.left) / 2));
mTransitionBundle.putFloat(TRANSITION_Y, (rect.top + (rect.bottom - rect.top) / 2) - (mRect.top + (mRect.bottom - mRect.top) / 2));
}
/**
* 模擬入場動畫
*/
private void runEnterAnim(View next_view,final View realNextView,final FrameLayout mContainer) {
next_view.animate()
.setInterpolator(new LinearInterpolator())
.setDuration(300)
.scaleX(mScaleBundle.getFloat(SCALE_WIDTH))
.scaleY(mScaleBundle.getFloat(SCALE_HEIGHT))
.translationX(mTransitionBundle.getFloat(TRANSITION_X))
.translationY(mTransitionBundle.getFloat(TRANSITION_Y))
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
realNextView.setVisibility(View.GONE);
if (mTransitionCustomListener!=null){
mTransitionCustomListener.onTransitionStart(animation);
}
}
@Override
public void onAnimationEnd(Animator animation) {
mContainer.setVisibility(View.GONE);
realNextView.setVisibility(View.VISIBLE);
if (mTransitionCustomListener!=null){
mTransitionCustomListener.onTransitionEnd(animation);
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (mTransitionCustomListener!=null){
mTransitionCustomListener.onTransitionCancel(animation);
}
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
6、界面返回時將創建的父容器重新添加至下一個頁面動畫實現,將創建的元素以動畫形式返回初始位置,結束後移除父容器
/**
* 模擬退場動畫
*/
public void exitActivity(final Activity activity) {
if (nResId!=-1 && mContainer!=null){
//先將創建的父容器從上一個頁面移除
((ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT)).removeView(mContainer);
activity.finish();
activity.overridePendingTransition(0,0);
//將創建的父容器重新添加至下一個頁面
((ViewGroup) mFirstActivity.findViewById(Window.ID_ANDROID_CONTENT)).addView(mContainer);
mContainer.setVisibility(View.VISIBLE);
//開始動畫
mContainer.getChildAt(0).animate()
.setInterpolator(new LinearInterpolator())
.setDuration(300)
.scaleX(1)
.scaleY(1)
.translationX(0)
.translationY(0)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mFirstView.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
mFirstView.setVisibility(View.VISIBLE);
mContainer.setVisibility(View.GONE);
//結束後移除父容器
((ViewGroup) mFirstActivity.findViewById(Window.ID_ANDROID_CONTENT)).removeView(mContainer);
mContainer.removeAllViews();
mContainer = null;
mFirstView = null;
mFirstActivity =null;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}else{
activity.finish();
activity.overridePendingTransition(0,0);
}
}
三、用兼容庫將開源項目MaterialLogin動畫效果兼容至Android4.0
這裡就簡單說下兼容MaterialLogin的實現,
首先,界面跳轉,調用TransitionController.getInstance().startActivity(this,new Intent(this, RegisterActivity.class),fab,R.id.fab);方法既可,
之後,跳轉至注冊頁面,調用TransitionController.getInstance().setEnterListener設置動畫監聽,在過渡動畫結束時,調用
ViewAnimationCompatUtils.createCircularReveal顯示圓形展開效果,最後返回調用
TransitionController.getInstance().exitActivity(RegisterActivity.this);,炫酷的登錄頁面就實現啦。
項目github地址:https://github.com/zhangke3016/TranslationCompat 歡迎star、fork。
ArrayMap的介紹官方對ArrayMap也有說明:它不是一個適應大數據的數據結構,相比傳統的HashMap速度要慢,因為查找方法是二分法,並且當你刪除或者添加數據時,
作為一只安卓自學的小白,今天第一天發表微博還是有點小激動的,好了,廢話少說下面開始我的安卓自定義控件知識總結。我的demo是一個自定義的TopBar,左邊一個Button
1.MVP簡介:隨著UI創建技術的功能日益增強,UI層也履行著越來越多的職責。為了更好地細分視圖(View)與模型(Model)的功能,讓View專注於處理數據的可視化以
一.前言之前已經將銀聯支付功能進行了集成,暫時將退款功能擱下了,今天抽了一小段光陰把這個洞給補上了。其實有了上一次集成支付功能的經驗,對退貨退款的集成就很容易實現了。本文