實(shí)現(xiàn)Android5.0過(guò)渡動(dòng)畫兼容庫(kù)

Android5.0之后為我們提供了許多炫酷的界面過(guò)渡效果,其中共享元素過(guò)渡也是很有亮點(diǎn)的一個(gè)效果,但這個(gè)效果只能在Android5.0之后使用,那今天我們就來(lái)將共享元素過(guò)渡效果兼容到Android4.0,讓5.0之前的手機(jī)也可以體驗(yàn)這么炫酷的效果吧。

A transition animation compatible Library.

兼容Android5.0之后轉(zhuǎn)場(chǎng)動(dòng)畫至Android4.0。

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

依慣例,首先來(lái)說(shuō)下本文的行文思路吧:
一、頁(yè)面過(guò)渡兼容庫(kù)的使用
二、頁(yè)面過(guò)渡兼容庫(kù)實(shí)現(xiàn)原理淺析
三、用兼容庫(kù)將開源項(xiàng)目MaterialLogin動(dòng)畫效果兼容至Android4.0

MaterialLogin

原項(xiàng)目地址:MaterialLogin
將動(dòng)畫效果兼容至Android4.0

Translation

Translation

一、頁(yè)面過(guò)渡兼容庫(kù)的使用

使用這個(gè)兼容庫(kù)也很簡(jiǎn)單,首先,在要控制跳轉(zhuǎn)的頁(yè)面調(diào)用TransitionController.getInstance().startActivity方法來(lái)實(shí)現(xiàn)跳轉(zhuǎn),在其中主要是傳入當(dāng)前界面要過(guò)渡到另一頁(yè)面的過(guò)渡元素View,以及另一個(gè)頁(yè)面對(duì)應(yīng)共享元素的View id值。
然后,在跳轉(zhuǎn)到的第二個(gè)頁(yè)面調(diào)用TransitionController.getInstance().show方法來(lái)實(shí)現(xiàn)元素的過(guò)渡,傳入?yún)?shù)也很簡(jiǎn)單。
最后呢,在頁(yè)面返回的時(shí)候,調(diào)用TransitionController.getInstance().exitActivity方法即可。
這樣一個(gè)完整的界面過(guò)渡動(dòng)畫基本就可以使用了,當(dāng)然,為了讓實(shí)現(xiàn)的效果更炫酷,加入了對(duì)過(guò)渡動(dòng)畫狀態(tài)的監(jiān)聽,可以在動(dòng)畫結(jié)束時(shí)加入自己的操作,為方便起見,兼容庫(kù)包含圓形元素過(guò)渡:調(diào)用ViewAnimationCompatUtils.createCircularReveal方法既可實(shí)現(xiàn)元素以圓形展開和收起,使用方式和ViewAnimationUtils類一致,以及矩形元素過(guò)渡:調(diào)用:ViewAnimationCompatUtils.createRectReveal方法既可實(shí)現(xiàn)元素以矩形方式以左、上、右、下四個(gè)方向展開。
具體代碼如下:

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

//跳轉(zhuǎn)后頁(yè)面調(diào)用:
TransitionController.getInstance().show(this,getIntent());
可在show方法調(diào)用之前設(shè)置監(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) {
            }
        });
//界面退出的時(shí)候調(diào)用
TransitionController.getInstance().exitActivity(PageDetailActivity.this);

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

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

二、頁(yè)面過(guò)渡兼容庫(kù)實(shí)現(xiàn)原理淺析

先講了這個(gè)兼容庫(kù)的用法,現(xiàn)在來(lái)聊聊它是怎么實(shí)現(xiàn)的,可以把主要實(shí)現(xiàn)細(xì)分六步:

1、獲取跳轉(zhuǎn)頁(yè)面過(guò)渡元素的位置
2、將跳轉(zhuǎn)過(guò)渡元素的位置傳給下一個(gè)頁(yè)面
3、在跳轉(zhuǎn)到的頁(yè)面獲取位置信息并創(chuàng)建相同寬高大小的元素和其覆蓋屏幕的父容器,并將新創(chuàng)建的元素添加到父容器中,而父容器添加至根視圖中
4、獲取跳轉(zhuǎn)到的頁(yè)面元素截圖并將其設(shè)為創(chuàng)建元素的背景
5、將當(dāng)前新元素位置與跳轉(zhuǎn)到頁(yè)面對(duì)比獲取縮放比例與移動(dòng)距離并開始動(dòng)畫,結(jié)束后將父容器隱藏
6、界面返回時(shí)將創(chuàng)建的父容器重新添加至下一個(gè)頁(yè)面動(dòng)畫實(shí)現(xiàn),將創(chuàng)建的元素以動(dòng)畫形式返回初始位置,結(jié)束后移除父容器

1、獲取跳轉(zhuǎn)頁(yè)面過(guò)渡元素的位置

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

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

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

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

View virtalView = new View(activity);
                Bitmap cacheBitmap = BitmapUtil.getCacheBitmapFromView(next_view);


                // 獲取上一個(gè)界面中,元素的寬度和高度
                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)到的頁(yè)面元素截圖并將其設(shè)為創(chuàng)建元素的背景

//獲取跳轉(zhuǎn)到的頁(yè)面元素截圖
Bitmap cacheBitmap = BitmapUtil.getCacheBitmapFromView(next_view);
//將其設(shè)為創(chuàng)建元素的背景
virtalView.setBackgroundDrawable(new BitmapDrawable(activity.getResources(), cacheBitmap));
/**
     * 獲取一個(gè) 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、將當(dāng)前新元素位置與跳轉(zhuǎn)到頁(yè)面對(duì)比獲取縮放比例與移動(dòng)距離并開始動(dòng)畫,結(jié)束后將父容器隱藏

      //將當(dāng)前新元素位置與跳轉(zhuǎn)到頁(yè)面對(duì)比獲取縮放比例與移動(dòng)距離
      getBundleInfo(next_view,mOriginWidth,mOriginHeight,mRect);
      //開始動(dòng)畫
      runEnterAnim(virtalView,next_view,mContainer);

       /**
     * 計(jì)算縮放比例,以及位移距離
     *
     * @param
     */
    private void getBundleInfo(View mView,int mOriginWidth,int mOriginHeight,Rect mRect) {
        // 計(jì)算縮放比例
        mScaleBundle.putFloat(SCALE_WIDTH, (float) mView.getWidth() / mOriginWidth);
        mScaleBundle.putFloat(SCALE_HEIGHT, (float) mView.getHeight() / mOriginHeight);

        Rect rect = new Rect();
        mView.getGlobalVisibleRect(rect);
        // 計(jì)算位移距離
        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));

    }

/**
     * 模擬入場(chǎng)動(dòng)畫
     */
    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、界面返回時(shí)將創(chuàng)建的父容器重新添加至下一個(gè)頁(yè)面動(dòng)畫實(shí)現(xiàn),將創(chuàng)建的元素以動(dòng)畫形式返回初始位置,結(jié)束后移除父容器

/**
     * 模擬退場(chǎng)動(dòng)畫
     */
    public void exitActivity(final Activity activity) {
        if (nResId!=-1 && mContainer!=null){
//先將創(chuàng)建的父容器從上一個(gè)頁(yè)面移除
            ((ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT)).removeView(mContainer);
            activity.finish();
            activity.overridePendingTransition(0,0);
            //將創(chuàng)建的父容器重新添加至下一個(gè)頁(yè)面
            ((ViewGroup) mFirstActivity.findViewById(Window.ID_ANDROID_CONTENT)).addView(mContainer);
            mContainer.setVisibility(View.VISIBLE);
            //開始動(dòng)畫
            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);
                            //結(jié)束后移除父容器
                            ((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);
        }
    }

三、用兼容庫(kù)將開源項(xiàng)目MaterialLogin動(dòng)畫效果兼容至Android4.0

這里就簡(jiǎn)單說(shuō)下兼容MaterialLogin的實(shí)現(xiàn),
首先,界面跳轉(zhuǎn),調(diào)用TransitionController.getInstance().startActivity(this,new Intent(this, RegisterActivity.class),fab,R.id.fab);方法既可,
之后,跳轉(zhuǎn)至注冊(cè)頁(yè)面,調(diào)用TransitionController.getInstance().setEnterListener設(shè)置動(dòng)畫監(jiān)聽,在過(guò)渡動(dòng)畫結(jié)束時(shí),調(diào)用ViewAnimationCompatUtils.createCircularReveal顯示圓形展開效果,最后返回調(diào)用TransitionController.getInstance().exitActivity(RegisterActivity.this);,炫酷的登錄頁(yè)面就實(shí)現(xiàn)啦。

項(xiàng)目github地址:https://github.com/zhangke3016/TranslationCompat 歡迎star、fork。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容