使用Circular Reveal動畫讓頁面跳轉更炫酷

Android 5.0中引入了很多炫酷的動畫效果,Circular Reveal便是其中一種。使用起來很簡單,但效果卻是意想不到的炫酷,讓你的app更有逼格。

一、效果

廢話不說,下面的gif圖中使用Circular Reveal動畫實現跳轉到搜索頁的效果。gif圖壓縮寬高比失真了,不過效果還在。源碼在最下面,可以下載體驗下。

二、Circular Reveal介紹

當您顯示或隱藏一組 UI 元素時,揭露動畫可為用戶提供視覺連續性。

ViewAnimationUtils.createCircularReveal()方法讓您能夠為裁剪區域添加動畫以揭露或隱藏視圖。

* @param view The View will be clipped to the animating circle.
     * @param centerX The x coordinate of the center of the animating circle, relative to
     *                <code>view</code>.
     * @param centerY The y coordinate of the center of the animating circle, relative to
     *                <code>view</code>.
     * @param startRadius The starting radius of the animating circle.
     * @param endRadius The ending radius of the animating circle.
     */
    public static Animator createCircularReveal(View view,
            int centerX,  int centerY, float startRadius, float endRadius) {
        return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
    }

ViewAnimationUtils.createCircularReveal()方法所執行的效果,就是將一個View裁剪成圓,然后從圓心逐漸揭露展現視圖。

參數 參數說明
view 要執行動畫效果的View
centerX 圓心x坐標
centerY 圓心y坐標
startRadius 開始時的圓半徑
endRadius 結束時的圓半徑

三、實現


從上圖可以看出,需要揭露展現的View是整個視圖的根布局。開始的位置就是??圖標的x,y坐標。開始的半徑為0,結束的半徑是上面那條斜邊的長度。知道了這些參數,那么實現就簡單了。
以下代碼使用Kotlin實現,不過和java區別不大,不影響看懂原理。

1.動畫參數

@SuppressLint("NewApi")
    private fun actionOtherVisible(isShow: Boolean, triggerView: View, animView: View) {
        //判斷API是否大于21
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
            if (isShow) {
                animView.visibility = View.VISIBLE
                if (mListener != null) mListener!!.onShowAnimationEnd()
            } else {
                animView.visibility = View.GONE
                if (mListener != null) mListener!!.onHideAnimationEnd()
            }
            return
        }

        /**
         * 計算 triggerView(即搜索按鈕) 的中心位置
         */
        val tvLocation = IntArray(2)
        triggerView.getLocationInWindow(tvLocation)
        val tvX = tvLocation[0] + triggerView.width / 2
        val tvY = tvLocation[1] + triggerView.height / 2

        /**
         * 計算 animView(即根布局) 的中心位置
         */
        val avLocation = IntArray(2)
        animView.getLocationInWindow(avLocation)
        val avX = avLocation[0] + animView.width / 2
        val avY = avLocation[1] + animView.height / 2
        //計算寬高
        val rippleW = if (tvX < avX) animView.width - tvX else tvX - avLocation[0]
        val rippleH = if (tvY < avY) animView.height - tvY else tvY - avLocation[1]
       //勾股定理求斜邊
        val maxRadius = Math.sqrt((rippleW * rippleW + rippleH * rippleH).toDouble()).toFloat()
        val startRadius: Float
        val endRadius: Float
        //根據展示或隱藏設置起始與結束的半徑
        if (isShow) {
            startRadius = 0f
            endRadius = maxRadius
        } else {
            startRadius = maxRadius
            endRadius = 0f
        }

        val anim = ViewAnimationUtils.createCircularReveal(animView, tvX, tvY, startRadius, endRadius)
        animView.visibility = View.VISIBLE
        anim.duration = DURATION
        anim.interpolator = DecelerateInterpolator()
        //監聽動畫結束,進行回調
        anim.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                if (isShow) {
                    animView.visibility = View.VISIBLE
                    if (mListener != null) mListener!!.onShowAnimationEnd()
                } else {
                    animView.visibility = View.GONE
                    if (mListener != null) mListener!!.onHideAnimationEnd()
                }
            }
        })

        anim.start()
    }

上述代碼中注釋清楚解析了動畫參數的獲取和執行過程。

2.動畫調用

fun show(triggerView: View, showView: View) {
        actionOtherVisible(true, triggerView, showView)
    }

fun hide(triggerView: View, hideView: View) {
        actionOtherVisible(false, triggerView, hideView)
    }

actionOtherVisible()方法根據傳入true/false來確定是執行展示或隱藏動畫。

3.動畫調用時機

在SearchFragment中,監聽第一幀的繪制,開啟動畫。其中mRootView就是根布局View。

override fun onPreDraw(): Boolean {
        iv_search_search.viewTreeObserver.removeOnPreDrawListener(this);
        mCircularRevealAnim.show(iv_search_search, mRootView);
        return true;
    }

動畫結束調用時機:①在點擊搜索,跳轉到搜索結果界面。②物理回退鍵回退。③點擊回退按鈕

再以上三個地方都可以調用hide()方法,實現隱藏動畫。

4.監聽回調

在上面配置動畫參數的過程中,對動畫結束進行了監聽回調。調用了AnimListener接口的onHideAnimationEnd()和onShowAnimationEnd()方法,來實現回調。所有在SearchFragment中實現該接口,來監聽回調。

override fun onHideAnimationEnd() {
    et_search_keyword.setText("");
    dismiss();
}

override fun onShowAnimationEnd() {
    if (isVisible) {
        KeyBoardUtils.openKeyboard(activity, et_search_keyword);
    }
}

監聽到隱藏動畫結束的時候,調用dismiss()方法關閉該DialogFragment。監聽展現動畫結束的時候,開啟輸入法框。

就是這么簡單,通過以上方式就可以實現如此炫酷的效果。

Github地址:搜索頁Circular Reveal動畫

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,252評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,532評論 25 708
  • 學校舉辦了一個活動,給父母做一餐美食,作為六一節反饋給父母的一種感恩。兒子接到老師的通知后躍躍欲試,對我說:媽...
    鄧啟旭鄧君浩媽媽閱讀 278評論 0 1
  • 大家好,我是靈犬零零一,我是五只靈犬宣傳委員會的一員。今天五只靈犬終于逆襲成功啦,為了拿獎還真是“不擇手段了呢”。...
    思敏_e2ec閱讀 193評論 0 0
  • 藝術無國界,藝術品有;音樂無國界,音樂家有;文學無國界,文學家有……所以,世界公民的定義永遠行不通,天下大同的想法...
    24e2f6668318閱讀 297評論 0 0