18 年底做 Readhub APP 時就加入了這個返回動畫效果。一直到現在,才有時間來簡單總結和封裝一下。
不知道這個返回手勢動畫到底是 MIUI 還是「即刻」APP 首創,因為我那會兒還沒用上 MIUI 全面屏。不過 MIUI 全面屏那個返回手勢相對簡單,固定位置。從這個效果上看,我覺得是 MIUI 先有這個效果,然后 「即刻」APP 優化豐富了一下。當然,可能還有另外一種情況,這是早就有的效果圖,只是在目前,我已知有使用的就上面兩個場景。
總結起來很簡單,就是一個「貝塞爾曲線」的繪制,再外加一個箭頭繪制。箭頭什么的的繪制在之前的倉庫中已經練習過很多。這次著重說說這個特殊圖形需要怎么繪制。
對于貝塞爾曲線繪制,之前玩過兩階、三階的。第一次看到這個效果,覺得貝塞爾曲能實現,但是這是幾階的,高階的怎么玩,那就是從頭開始。
在經過一番把玩探索后,最終確定這就是五階的貝塞爾曲線, Android
似乎默認沒有高階對應的 api 。
那這怎么辦呢?曲線就是函數嘛,肯定有公式,網上就搜到公式寫法。
private fun calculateY(i: Int, j: Int, t: Float): Float {
return if (i == 1) {
(1 - t) * controlPoints[j].y + t * controlPoints[j + 1].y
} else (1 - t) * calculateY(i - 1, j, t) + t * calculateY(i - 1, j + 1, t)
}
controlPoints 對應的就是那個五個控制點的集合。
addControlPoint(0, 0f, yResult - maxPeakValue * 1.5f)
addControlPoint(1, 0f, yResult - maxPeakValue * 1.5f * GOLDEN_RATIO)
addControlPoint(2, min, yResult)
addControlPoint(3, 0f, yResult + maxPeakValue * 1.5f * GOLDEN_RATIO)
addControlPoint(4, 0f, yResult + maxPeakValue * 1.5f)
接著就是控制是否攔截事件,我現在是做成 Helper 這種工具類型,和對應的 ViewGroup
是解耦的,其實就是 ViewDragHelper
的一個簡單實現。
對了,最后考慮下來,實現了左右兩邊的滑動效果。至于上下,我覺得這種場景不大,就懶得去做了。
具體代碼細節就不貼了,源碼也沒多少,這里講遇到的一些細節問題或者寫出來的 bug
返回退出應用后,后臺程序預覽中存在返回手勢效果
這里其實就是一個先后問題,一開始是同步執行 invalidate()
和 onBackReleased()
。后來使用 Runnable
+ postDalay()
來延遲 onBackReleased()
執行。
貝塞爾曲線貼近屏幕的地方總有一個像素的白線
這里后面排查出來是我添加控制點,for 循環時角標是[0,length-1],最后一個沒有添加計算到,所以計算出來的控制點就少了一個。最終效果就是繪制出來的圖形 x 軸沒有完全對稱。偏差就在 1px 左右。
最后的效果就是總感覺下方有一跟白線。
for 循環處理好之后,發現右邊繪制出來還是會有這個情況,具體原因不清楚,因為單看數據層面,它肯定是貼邊的。最后很討巧,使用到 translate()
將 Canvas
平移一個像素規避掉,這簡直是程序員的小巧思,哈哈 ??。
控制點數量及緩存
因為是用公式算的控制點,所以每一次繪制,其實簡單理解就是講一個一個點連接成一條曲線的。那么問題就是,點多,曲線當然最逼真,但是單位時間處理的數據量就上去了。
點少,可能你看到就是折線圖了。
最后均衡在 50 個控制點,點與點之間的比例就是 2% 。在繪制等方法中肯定不能頻繁創建對象,所以這 50 個點需要復用。
倉庫地址
lovejjfg/SwipeBack 現已同步 jcenter 。 詳情請移步 README.md