GIF動圖效果不是很好,實際效果很是平滑very smooth,並且添加不一樣的圖形能夠組成各類效果,目前已經用在咱們項目的註冊界面~java
實現原理很簡單,每個懸浮的「小物體」就是一個自定義View,這些小的自定義View都盛放在一個自定義的ViewGroup中。而後全部的視圖都放在這個ViewGroup之上,這樣就至關於作一個可動的背景。android
下面結合代碼詳細介紹下:git
懸浮的物體,繼承自View,須要重寫onDraw方法,主要做用就是來畫出本身,並進行隨機曲線運動。github
任何須要畫出的對象都須要繼承FloatObject,並重寫提供的drawFloatObject方法,在此方法中能夠經過設置畫筆和畫布畫出任意圖形。好比下面是畫出一行文字:算法
public class FloatText extends FloatObject { String text; public FloatText(float posX, float posY, String text) { super(posX, posY); this.text = text; setAlpha(88); setColor(Color.WHITE); } @Override public void drawFloatObject(Canvas canvas, float x, float y, Paint paint) { paint.setTextSize(65); canvas.drawText(text, x, y, paint); } }
其實最複雜的部分就是讓漂浮的物體作隨機無規則的曲線運動,而且每一個漂浮物的速度不一樣,這樣整個漂浮動畫才更加天然。canvas
我以前想過使用布朗運動,可是在網上找了很久也沒找到一個好用的算法。數據結構
最後只能仍是使用3點賽貝爾曲線,使漂浮物沿着一條賽貝爾曲線運動,達到終點時,再隨機產生一條新的曲線,這樣就能夠實現隨機曲線運動了。dom
控制運動的代碼以下:ide
public void drawFloatItem(Canvas canvas) { switch (status) { case START: // fade in if (isFade() && alpha <= ALPHA_LIMIT) { paint.setAlpha(alpha); alpha += ALPHA_PER_FRAME; } else { setStatus(MOVE); } break; case MOVE: // 更新賽貝爾曲線點 if (mCurDistance == 0) { start = new PointF(x, y); end = getRandomPoint((int)start.x, (int)start.y, (int) distance);// 取值範圍distance c1 = getRandomPoint((int)start.x, (int)start.y, random.nextInt(width / 2)); // 取值範圍width/2 c2 = getRandomPoint(end.x, end.y, random.nextInt(width / 2));// 取值範圍width/2 } // 計算塞貝兒曲線的當前點 PointF bezierPoint = CalculateBezierPoint(mCurDistance / distance, start, c1, c2, end); x = bezierPoint.x; y = bezierPoint.y; // 更新當前路徑 mCurDistance += MOVE_PER_FRAME; // 一段畫完後重置 if (mCurDistance >= distance) { mCurDistance = 0; } break; case END: // fade out if (isFade() && alpha > 0) { paint.setAlpha(alpha); alpha -= ALPHA_PER_FRAME; } else { setStatus(FINISH); } break; } if (status != FINISH) { Log.e("drawFloatObject", x+", "+y); drawFloatObject(canvas, x ,y, paint); } }
關於賽貝爾曲線運動的算法都是複用以前寫的一篇文章ANDROID模擬火花粒子的滑動噴射效果,若是你們有興趣能夠看看。post
FloatBackground繼承自FrameLayout,裏面有一個用於存放FloatObject的集合。
FloatBackground的主要做用就是繪製全部的「漂浮物」,以及維護其生命週期:
private void initFloatObject(int width, int height) { for (FloatObject floatObject : floats) { int x = (int) (floatObject.posX * width); int y = (int) (floatObject.posY * height); floatObject.init(x, y, width, height); } }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (FloatObject floatObject : floats) { floatObject.drawFloatItem(canvas); } // 隔一段時間重繪一次, 動畫效果 getHandler().postDelayed(runnable, DELAY); } // 重繪線程 private Runnable runnable = new Runnable() { @Override public void run() { invalidate(); // 控制幀數 } };
public void startFloat() { for (FloatObject floatObject : floats) { floatObject.setStatus(FloatObject.START); } } public void endFloat() { for (FloatObject floatObject : floats) { floatObject.setStatus(FloatObject.END); } }
使用時很是簡單,在layout文件中將FloatBackground設置爲最底層的視圖(其實就是看成一個背景):
<com.dean.library.FloatBackground android:id="@+id/float_view" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Start" /> <Button android:id="@+id/end" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="End" /> </LinearLayout> </com.dean.library.FloatBackground>
在代碼中進行以下調用:
final FloatBackground floatBackground = (FloatBackground) this.findViewById(R.id.float_view); Button start = (Button) this.findViewById(R.id.start); start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { floatBackground.startFloat(); } }); Button end = (Button) this.findViewById(R.id.end); end.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { floatBackground.endFloat(); } }); floatBackground.addFloatView(new FloatRect(0.2f, 0.3f, 30, 40)); floatBackground.addFloatView(new FloatBitmap( this, 0.2f, 0.3f, R.drawable.gr_ptn_03)); floatBackground.addFloatView(new FloatCircle( 0.8f, 0.8f)); floatBackground.addFloatView(new FloatText( 0.3f, 0.6f, "E")); floatBackground.addFloatView(new FloatRing( 0.6f, 0.2f, 15 ,20));
好比在添加文字「漂浮物」時:floatBackground.addFloatView(new FloatText( 0.3f, 0.6f, 「E」))
接收的三個參數分別爲出生位置在屏幕寬的百分比,長的百分比,和顯示的文字。