編輯:關於Android編程
最近沒事的時候想自己寫一個支持下拉刷新,上拉加載的自定義View。寫著寫著,就覺得最常見的“一個圈轉啊轉”的進度條太普通了。於是,就想看看有沒有更有趣的一點的加載效果。在GitHub上以”android loading”為關鍵字一搜索,就發現有作者開源了這麼一個庫:
庫的地址是:https://github.com/ldoublem/LoadingView。裡面提供了很多有趣的加載動畫(非常棒),個人對其中如下一個效果產生了興趣:
那麼,開源的好處就來了,立刻打開源碼瞧一瞧別人是怎麼實現的吧。一看發現沒有借助任何圖片,而就是通過canvas配合屬性動畫完成的整個效果。
按理說別人造好的輪子,我們直接拿來用就好了。但既然感興趣,為什麼不學習一下別人的思路,自己也來實現一個,從而得到提高呢?所以,綜合一想,自己也重新來畫一畫這個萌蠢萌蠢的小鬼吧。並通過此文來總結一下整個自定義view的思路和收獲。
(P.S:會借鑒原作者的思路,但具體實現細節會有不同,但思路當然才是最重要的,具體實現選擇自己喜歡的就好)
其實說起繪畫,就想起了小時候流行的一個口訣,是畫“丁老頭”的,印象中有“一個丁老頭兒,借我倆煤球兒,我說三天還,他說四天還..”之類的。其實就是這樣的,如果猛的一下讓我們畫個“老頭兒”出來,我們可能會有點懵逼。但按照口訣那樣一部分一部分的畫,似乎就變得容易多了。
所以,我們也可以模仿這個思路來畫這個小鬼。我們簡單分析一下,可以發現這個小鬼的構成其實就是:頭 + 眼睛 + 身體 + 影子。那麼,還等什麼呢?趕緊按照這個思路“開畫”吧!首先,我們當然是新建一個類,並讓其繼承View,而名字的話就叫GhostView好了。
在正式開始“作畫”之前,我們肯定是做好相關的准備工作。比如,先確定好要用多大尺寸的“畫紙”。哈哈,其實也就是完成View的measure工作。我們知道自定義View的時候,如果使用默認的onMeasure()方法:WRAP_CONTENT也會被當做MATCH_PARENT來測量,所以其實要做的也很簡單:
。。。。。。。。好吧,目前為止我們還看不到任何“萌蠢小鬼”的跡象。沒關系,一步一步的來。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxociAvPg0KPGgyIGlkPQ=="誰說鬼就沒影子">誰說“鬼”就沒影子
有了小鬼的頭之後,我們接著做什麼呢?正常來說我們應該想著接著畫身體。但是從之前的效果圖我們可以看到,小鬼的底部是有一個影子的。所以,個人選擇先畫這個影子。因為:影子位於View的底部,先完成影子的繪畫,之後更方面我們確定小鬼身體的高度和位置。
其實所謂的影子也非常的簡單,就是一個灰蒙蒙的“橢圓形”而已:
// 影子所占區域 private RectF mRectShadow; // 小鬼身體和影子之間的舉例 private int paddingShadow; private void drawShadow(Canvas canvas) { paddingShadow = mHeight / 10; mRectShadow = new RectF(); mRectShadow.top = mHeight * 8 / 10; mRectShadow.bottom = mHeight * 9 / 10; mRectShadow.left = mWidth / 4; mRectShadow.right = mWidth * 3 / 4; canvas.drawArc(mRectShadow, 0, 360, false, mShadowPaint); }
這個時候,我們再來看一看效果變成了什麼樣子:
現在,我們就來到了最關鍵的部分了:為小鬼加上身體。其實總的來說,小鬼的身體就是在頭部大約半圓的位置,分別畫上兩條帶有弧度的延長線。但是,怎麼才能讓小鬼身體的這兩條線與頭部比較完美的融合呢?原作者在這裡使用了一些正弦、余弦的公式來計算圓的弧度,從而完成了需要。然而,悔不及當初沒有好好念書啊,患上了暈“數學公式”的病。所以我機智的選擇用另一種方法,雖然沒那麼高大上,但是比較簡單。就像下面這樣:
private Path mPath = new Path(); // 小鬼身體胖過頭部的寬度 private int mGhostBodyWSpace; private void drawBody(Canvas canvas) { mGhostBodyWSpace = mHeadRadius * 2 / 15; // 先畫右邊的身體 mPath.moveTo(mHeadLeftX, mHeadCentreY); mPath.lineTo(mHeadRightX, mHeadCentreY); mPath.quadTo(mHeadRightX + mGhostBodyWSpace, mRectShadow.top - paddingShadow, mHeadRightX - mGhostBodyWSpace, mRectShadow.top - paddingShadow); canvas.drawPath(mPath,mBodyPaint); }
上圖中左邊的部分就是我們目前為止得到的效果;而右邊就是通過把畫筆設置為stroke來解釋這樣做的原理,實際上就是:先通過lineTo在小鬼頭部的中間畫一條直徑,這個時候path的LastPoint就到了最右邊的這個點,然後我們從這個點在右邊向下畫一條二階貝塞爾曲線,就有了小鬼右邊身體的輪廓了。
那麼接著我們該做什麼呢?回憶一下,我們發現小鬼的身體下方是有“波紋”的,就想裙子的褶皺一樣,所以我們現在就給添上裙子。其實原理仍然很簡單,這個時候path的LastPoint也已經移動到了小鬼右邊身體的下面,我們從這裡開始向左不斷畫多個貝塞爾曲線形成裙褶就行了:
// 單個裙褶的寬高 private int mSkirtWidth, mSkirtHeight; // 裙褶的個數 private int mSkirtCount = 7; private void drawBody(Canvas canvas) { mGhostBodyWSpace = mHeadRadius * 2 / 15; mSkirtWidth = (mHeadRadius * 2 - mGhostBodyWSpace * 2) / mSkirtCount; mSkirtHeight = mHeight / 16; // ...... // 從右向左畫裙褶 for (int i = 1; i <= mSkirtCount; i++) { if (i % 2 != 0) { mPath.quadTo(mHeadRightX - mGhostBodyWSpace - mSkirtWidth * i + (mSkirtWidth / 2), mRectShadow.top - paddingShadow - mSkirtHeight, mHeadRightX - mGhostBodyWSpace - (mSkirtWidth * i), mRectShadow.top - paddingShadow); } else { mPath.quadTo(mHeadRightX - mGhostBodyWSpace - mSkirtWidth * i + (mSkirtWidth / 2), mRectShadow.top - paddingShadow + mSkirtHeight, mHeadRightX - mGhostBodyWSpace - (mSkirtWidth * i), mRectShadow.top - paddingShadow); } } canvas.drawPath(mPath,mBodyPaint); }
可以看到到了現在,基本就能看見整個小鬼的輪廓了,但我們注意到小鬼左邊似乎有點僵硬。沒關系,我們也給他加上一點對應的弧度就行了:
mPath.quadTo(mHeadLeftX - mGhostBodyWSpace, mRectShadow.top - paddingShadow, mHeadLeftX, mHeadCentreY);
到了現在,我們的繪圖工作其實基本就已經完成了。但眼睛是心靈的窗戶,少了眼睛,這個小鬼看上去有點四不像的感覺。趕緊加上眼睛吧!
同樣的,眼睛的繪制其實也非常簡單,就在先要的位置,畫上兩個黑色的小圓就可以了:
private void drawEyes(Canvas canvas) { canvas.drawCircle(mHeadCentreX , mHeadCentreY, mHeadRadius / 6, mEyesPaint); canvas.drawCircle(mHeadCentreX + mHeadRadius / 2, mHeadCentreY, mHeadRadius / 6, mEyesPaint); }
現在我們所有的繪制工作就完成了,把之前粉紅色的背景顏色去掉,再看看效果,是不是有點呆萌的趕腳了呢?
現在小鬼我們已經畫完了,剩下的工作自然就是讓它動起來,別死氣沉沉的。而我們已經知道了,這個工作就是通過屬性動畫來完成的。
那麼,我們可以添加一個最簡單的位移動畫,比如說這樣做:
private void startAnim(){ ObjectAnimator animator = ObjectAnimator.ofFloat(this,"translationX",0,500); animator.setRepeatMode(ObjectAnimator.RESTART); animator.setRepeatCount(ObjectAnimator.INFINITE); animator.setDuration(5000); animator.start(); } @Override protected void onDraw(Canvas canvas) { // ...... startAnim(); }
可以看到這樣“小鬼”就已經動起來了,不過現在它肯定沒有那麼萌了。因為它的行進路徑和恐怖片裡那些白衣幽靈看上去一樣一樣的。不過這裡主要是表達個意思嘛,要實現作者原本的那個動畫效果實際上也不難,我們分析一下可以發現它主要有幾個動作:就是小鬼在行進的同時還會上下跳動,並且底部的影子會隨著小鬼跳起和落下而改變大小,那麼我們就可以借助ValueAnimator來實現。
簡單來說,要做的工作就是之前描繪小鬼時的相關屬性(例如小鬼的頭部的圓心坐標,影子的rect的寬度等)不要寫死,而是與某個值產生關聯。然後我們用ValueAnimator來監聽和不斷的改變這個值,然後讓view不斷重繪,就可以得到響應的一些動畫效果了。
一.啟動動畫的漸變:以後可能會用到的啟動動畫的效果:效果圖: 主界面: public class MainActivity extends Activity {
1、rk3168_v4.2\frameworks\base\data\videos下面的mp4的拷貝方法! a、其實在我們的原始情況下這個目錄的東西並沒有拷貝到xxx/s
加速篇GRADLE的構建過程通常會比較漫長,一個中等項目,10M左右大小的app,一次完整構建大概在5分鐘左右,是不是很嚇人,當然,如果是在調試階段,采用Android
Android Volley 是Google開發的一個網絡lib,可以讓你更加簡單並且快速的訪問網絡數據。Volley庫的網絡請求都是異步的,你不必擔心異步處理問題。Vo