Android 動畫初探
前言:
好久沒來寫文章了,一方面是因為自己懶了,另外一個是因為最近工作比較忙,沒有閒時間(其實主要還是因為懶)。話說八月多換了一個新工作,在之前的公司,主要是橫向發展,瞭解了很多技術。在現在的公司主要是縱向發展,更加深入的探索。之前是廣而不精,沒有深入學習。在現在的公司呢,能夠更加深入的學習技術。就拿最近的工作來說吧,動畫很多,剛開始只是知道Android動畫分為屬性動畫、幀動畫和補間動畫。但是,他們之間有什麼具體的區別就不是特別清楚了。尤其是屬性動畫和補間動畫的區別,我能說自己被補間動畫坑慘了嗎?(其實,還是自己學藝不精,需要繼續努力了。)好了,廢話說多了,還是來看看今天的文章吧。
首先
要學習Android動畫,我們以一個例子來深入學習,畢竟都是要把功能實現出來,寫一些大而空沒有實用性的東西,不但是敷衍別人,更是對自己的不負責任。如下圖所示:(原諒我自戀一下,放了自己的影象照)

Screenshot_20181023-200259.png

Screenshot_20181023-200103.png
-
分析
1、要實現從大圖到小圖的過度,我們需要怎麼實現呢?又需要用哪些技術呢?首先圖片縮小了,那麼就需要縮放動畫;其次,圖片向右移動和向上移動了,那麼就需要位移動畫。背景動畫是用Lottie實現的,中間的圓形變叉也是用的lottie監聽幀率變化,這個不在今天的討論和實現範圍內。今天主要實現佈局從大到小的縮放和移動。
2、 那麼縮放和移動可以用哪些動畫實現呢?可能有朋友說是用補間動畫實現了,那麼如果佈局上面有點選事件呢?點選事件的位置在原位置還是新位置呢?答案是還在原來的位置,補間動畫只能實現view的變化,而不能改變點選事件的位置,如果只是做一個頁面展示用,那麼補間動畫完全夠用了。但是,如果想改變點選事件的位置,必須要用屬性動畫。我能說我在這裡被坑了好久嗎?好了,現在來看看我們如何實現控制元件的縮放和移動。
其次
/** * 動畫 */ public static void scanAnimation(Context context, FrameLayout mLytAll) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); final int width = dm.widthPixels;// 螢幕寬度(畫素) int height = dm.heightPixels;// 螢幕高度(畫素) ObjectAnimator translationX = ObjectAnimator.ofFloat(mLytAll, "translationX", 1, (DensityUtils.px2dp(context, width) - 36)); ObjectAnimator translationY = ObjectAnimator.ofFloat(mLytAll, "translationY", 1, -(DensityUtils.px2dp(context, height) - 23)); ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLytAll, "scaleX", (94 / DensityUtils.px2dp(context, width))); ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLytAll, "scaleY", (166 / DensityUtils.px2dp(context, height))); AnimatorSet animatorSet = new AnimatorSet();//組合動畫 animatorSet.playTogether(translationX, translationY, scaleX, scaleY); //設定動畫 animatorSet.setDuration(350); animatorSet.start(); }
-
分析:
1、這裡要移動,那麼就需要螢幕的寬和高,然後進行等比縮放,因為上面和右邊有一定的距離,那麼就要計算右邊距和上邊距;
2、這裡因為是X軸和Y軸進行移動並且縮放,所以我這邊用了組合動畫,AnimatorSet;
3、X軸的移動動畫用了ObjectAnimator然後設定X軸移動和Y軸移動,。假設,距離右邊距是36dp,上邊距是23。那麼X軸的移動就是,從1到螢幕寬轉換成dp值後減去右邊距(36dp)。Y軸的移動就是螢幕的高轉換為dp後減去上邊距(23dp),然後加上一個負號(-),為什麼呢,因為Y軸向上為負,向下為正;
4、現在來看縮放動畫,假設縮放後的小螢幕高是166dp,寬是94dp,那麼就是縮放後的螢幕寬(94dp)除以螢幕寬轉換成dp值之後的比率了。當然,這樣做顯然是不太嚴謹的(為什麼說不嚴謹呢,後面會講)。Y軸的縮放呢就是縮放後小圖的高(166dp)除以螢幕高轉換成dp之後的值。
5、完成後用AnimatorSet設定X軸Y軸的平移和縮放動畫同步進行,然後設定動畫的執行時長至此就完成了動畫從大螢幕到小螢幕的移動和縮放。
再次
之前在分析的時候說上面直接寫死寬高值之後進行縮放不嚴謹,那麼我們來分析一下。因為Android手機螢幕解析度很多,如果在大螢幕手機上面可能不會出現太大的問題,那麼在螢幕解析度比較低的手機上呢?有可能顯示不全或者按鈕擠壓到一起去了。那麼,就要動態計算小螢幕的寬高了。比如說UI給了我們的UI圖的寬高比是360 640,那麼我們如何來動態計算螢幕的寬高比呢?那麼,X軸的縮放比率就不應該是94/DensityUtils.px2dp(context, width))了,而應該是(94 (DensityUtils.px2dp(context, width))/360))/DensityUtils.px2dp(context, width)),就是說我們應該計算UI圖的寬和手機實際的寬的比率乘以94再除以螢幕的寬,這才是我們需要縮放的寬。那麼高呢?當然是一樣的了166 / DensityUtils.px2dp(context, height)),這樣寫當然也是有問題的了,而應該是(166*(DensityUtils.px2dp(context, height))/640))/DensityUtils.px2dp(context, height)),這樣才是更加嚴謹的寫法。
/** * 動畫 */ public static void scanAnimation(Context context, FrameLayout mLytAll) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); final int width = dm.widthPixels;// 螢幕寬度(畫素) int height = dm.heightPixels;// 螢幕高度(畫素) ObjectAnimator translationX = ObjectAnimator.ofFloat(mLytAll, "translationX", 1, (DensityUtils.px2dp(context, width) - 36)); ObjectAnimator translationY = ObjectAnimator.ofFloat(mLytAll, "translationY", 1, -(DensityUtils.px2dp(context, height) - 23)); ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLytAll, "scaleX", ((94(DensityUtils.px2dp(context, width)/360)) / DensityUtils.px2dp(context, width))); ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLytAll, "scaleY", ((166(DensityUtils.px2dp(context, height)/640)) / DensityUtils.px2dp(context, height))); AnimatorSet animatorSet = new AnimatorSet();//組合動畫 animatorSet.playTogether(translationX, translationY, scaleX, scaleY); //設定動畫 animatorSet.setDuration(350); animatorSet.start(); }
最後
好了,今天就寫到這裡,如果有不正確的還希望各位指正,小可這裡不勝感激,還有就是,能用屬性動畫實現的,儘量不要用補間動畫來做,因為你會碰到各種坑。