實現(xiàn)Android5.0過渡動畫兼容庫

Android5.0之后為我們提供了許多炫酷的界面過渡效果驻子,其中共享元素過渡也是很有亮點的一個效果飘庄,但這個效果只能在Android5.0之后使用慈缔,那今天我們就來將共享元素過渡效果兼容到Android4.0,讓5.0之前的手機也可以體驗這么炫酷的效果吧秀睛。

A transition animation compatible Library.

兼容Android5.0之后轉(zhuǎn)場動畫至Android4.0币他。

github地址:https://github.com/zhangke3016/TranslationCompat

依慣例坞靶,首先來說下本文的行文思路吧:
一、頁面過渡兼容庫的使用
二蝴悉、頁面過渡兼容庫實現(xiàn)原理淺析
三彰阴、用兼容庫將開源項目MaterialLogin動畫效果兼容至Android4.0

MaterialLogin

原項目地址:MaterialLogin
將動畫效果兼容至Android4.0

Translation

Translation

一、頁面過渡兼容庫的使用

使用這個兼容庫也很簡單辫封,首先硝枉,在要控制跳轉(zhuǎn)的頁面調(diào)用TransitionController.getInstance().startActivity方法來實現(xiàn)跳轉(zhuǎn),在其中主要是傳入當前界面要過渡到另一頁面的過渡元素View倦微,以及另一個頁面對應共享元素的View id值妻味。
然后,在跳轉(zhuǎn)到的第二個頁面調(diào)用TransitionController.getInstance().show方法來實現(xiàn)元素的過渡欣福,傳入?yún)?shù)也很簡單责球。
最后呢,在頁面返回的時候拓劝,調(diào)用TransitionController.getInstance().exitActivity方法即可雏逾。
這樣一個完整的界面過渡動畫基本就可以使用了,當然郑临,為了讓實現(xiàn)的效果更炫酷栖博,加入了對過渡動畫狀態(tài)的監(jiān)聽,可以在動畫結束時加入自己的操作厢洞,為方便起見仇让,兼容庫包含圓形元素過渡:調(diào)用ViewAnimationCompatUtils.createCircularReveal方法既可實現(xiàn)元素以圓形展開和收起典奉,使用方式和ViewAnimationUtils類一致,以及矩形元素過渡:調(diào)用:ViewAnimationCompatUtils.createRectReveal方法既可實現(xiàn)元素以矩形方式以左丧叽、上卫玖、右、下四個方向展開踊淳。
具體代碼如下:

//參數(shù)一:當前Activity
//參數(shù)二:跳轉(zhuǎn)意圖
//參數(shù)三:當前頁面跳轉(zhuǎn)至下一頁面的View
//參數(shù)四:下一頁面關聯(lián)的View id
TransitionController.getInstance().startActivity(this,new Intent(this, RegisterActivity.class),fab,R.id.fab);

//跳轉(zhuǎn)后頁面調(diào)用:
TransitionController.getInstance().show(this,getIntent());
可在show方法調(diào)用之前設置監(jiān)聽:
 TransitionController.getInstance().setEnterListener(new TransitionCustomListener() {
            @Override
            public void onTransitionStart(Animator animator) {
            }
            @Override
            public void onTransitionEnd(Animator animator) {
            }
            @Override
            public void onTransitionCancel(Animator animator) {
            }
        });
//界面退出的時候調(diào)用
TransitionController.getInstance().exitActivity(PageDetailActivity.this);

//增加界面圓形轉(zhuǎn)換動畫 
// 用法及參數(shù)和ViewAnimationUtils一致
ViewAnimationCompatUtils.createCircularReveal(cvAdd, cvAdd.getWidth()/2,0, fab.getWidth() / 2, cvAdd.getHeight());

//增加界面矩形轉(zhuǎn)換動畫 
Animator mAnimator = ViewAnimationCompatUtils.createRectReveal( nsv, 0, nsv.getHeight(),ViewAnimationCompatUtils.RECT_TOP);

二假瞬、頁面過渡兼容庫實現(xiàn)原理淺析

先講了這個兼容庫的用法,現(xiàn)在來聊聊它是怎么實現(xiàn)的迂尝,可以把主要實現(xiàn)細分六步:

1脱茉、獲取跳轉(zhuǎn)頁面過渡元素的位置
2、將跳轉(zhuǎn)過渡元素的位置傳給下一個頁面
3雹舀、在跳轉(zhuǎn)到的頁面獲取位置信息并創(chuàng)建相同寬高大小的元素和其覆蓋屏幕的父容器芦劣,并將新創(chuàng)建的元素添加到父容器中,而父容器添加至根視圖中
4说榆、獲取跳轉(zhuǎn)到的頁面元素截圖并將其設為創(chuàng)建元素的背景
5、將當前新元素位置與跳轉(zhuǎn)到頁面對比獲取縮放比例與移動距離并開始動畫寸认,結束后將父容器隱藏
6签财、界面返回時將創(chuàng)建的父容器重新添加至下一個頁面動畫實現(xiàn),將創(chuàng)建的元素以動畫形式返回初始位置偏塞,結束后移除父容器

1唱蒸、獲取跳轉(zhuǎn)頁面過渡元素的位置

//rect 來存儲共享元素位置信息
Rect rect = new Rect();
// 獲取元素位置信息
view.getGlobalVisibleRect(rect);

2、將跳轉(zhuǎn)過渡元素的位置傳給下一個頁面

// 將位置信息附加到 intent 上
intent.setSourceBounds(rect);
intent.putExtra(TRANSITION_NEXT_ID, nextShowViewId);

3灸叼、在跳轉(zhuǎn)到的頁面獲取位置信息并創(chuàng)建相同寬高大小的元素和其覆蓋屏幕的父容器神汹,并將新創(chuàng)建的元素添加到父容器中,而父容器添加至根視圖中

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);
                //創(chuàng)建覆蓋屏幕的父容器
                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));
                //創(chuàng)建相同寬高大小的元素
                virtalView.setLayoutParams(params);
                //將新創(chuàng)建的元素添加到父容器中
                mContainer.addView(virtalView);

4屁魏、獲取跳轉(zhuǎn)到的頁面元素截圖并將其設為創(chuàng)建元素的背景

//獲取跳轉(zhuǎn)到的頁面元素截圖
Bitmap cacheBitmap = BitmapUtil.getCacheBitmapFromView(next_view);
//將其設為創(chuàng)建元素的背景
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、將當前新元素位置與跳轉(zhuǎn)到頁面對比獲取縮放比例與移動距離并開始動畫捉腥,結束后將父容器隱藏

      //將當前新元素位置與跳轉(zhuǎn)到頁面對比獲取縮放比例與移動距離
      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、界面返回時將創(chuàng)建的父容器重新添加至下一個頁面動畫實現(xiàn)抵碟,將創(chuàng)建的元素以動畫形式返回初始位置桃漾,結束后移除父容器

/**
     * 模擬退場動畫
     */
    public void exitActivity(final Activity activity) {
        if (nResId!=-1 && mContainer!=null){
//先將創(chuàng)建的父容器從上一個頁面移除
            ((ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT)).removeView(mContainer);
            activity.finish();
            activity.overridePendingTransition(0,0);
            //將創(chuàng)建的父容器重新添加至下一個頁面
            ((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的實現(xiàn)拟逮,
首先撬统,界面跳轉(zhuǎn),調(diào)用TransitionController.getInstance().startActivity(this,new Intent(this, RegisterActivity.class),fab,R.id.fab);方法既可敦迄,
之后恋追,跳轉(zhuǎn)至注冊頁面凭迹,調(diào)用TransitionController.getInstance().setEnterListener設置動畫監(jiān)聽,在過渡動畫結束時几于,調(diào)用ViewAnimationCompatUtils.createCircularReveal顯示圓形展開效果蕊苗,最后返回調(diào)用TransitionController.getInstance().exitActivity(RegisterActivity.this);,炫酷的登錄頁面就實現(xiàn)啦。

項目github地址:https://github.com/zhangke3016/TranslationCompat 歡迎star沿彭、fork朽砰。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市喉刘,隨后出現(xiàn)的幾起案子瞧柔,更是在濱河造成了極大的恐慌,老刑警劉巖睦裳,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件造锅,死亡現(xiàn)場離奇詭異,居然都是意外死亡廉邑,警方通過查閱死者的電腦和手機哥蔚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛛蒙,“玉大人糙箍,你說我怎么就攤上這事∏K睿” “怎么了深夯?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诺苹。 經(jīng)常有香客問我咕晋,道長,這世上最難降的妖魔是什么收奔? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任掌呜,我火速辦了婚禮,結果婚禮上筹淫,老公的妹妹穿的比我還像新娘站辉。我一直安慰自己,他們只是感情好损姜,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布饰剥。 她就那樣靜靜地躺著,像睡著了一般摧阅。 火紅的嫁衣襯著肌膚如雪汰蓉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天棒卷,我揣著相機與錄音顾孽,去河邊找鬼祝钢。 笑死,一個胖子當著我的面吹牛若厚,可吹牛的內(nèi)容都是我干的拦英。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼测秸,長吁一口氣:“原來是場噩夢啊……” “哼疤估!你這毒婦竟也來了?” 一聲冷哼從身側響起霎冯,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铃拇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后沈撞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慷荔,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年缠俺,在試婚紗的時候發(fā)現(xiàn)自己被綠了显晶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡壹士,死狀恐怖吧碾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情墓卦,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布户敬,位于F島的核電站落剪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏尿庐。R本人自食惡果不足惜忠怖,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抄瑟。 院中可真熱鬧凡泣,春花似錦、人聲如沸皮假。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惹资。三九已至贺纲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間褪测,已是汗流浹背猴誊。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工潦刃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人懈叹。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓乖杠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澄成。 傳聞我的和親對象是個殘疾皇子胧洒,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容