ANDROID-漂浮背景效果
效果圖:
GIF動圖效果不是很好,實際效果非常平滑very smooth,而且新增不同的圖形可以組成各種效果,目前已經用在我們專案的註冊介面~
原理:
實現原理很簡單,每一個懸浮的“小物體”就是一個自定義View,這些小的自定義View都盛放在一個自定義的ViewGroup中。然後所有的檢視都放在這個ViewGroup之上,這樣就相當於做一個可動的背景。
下面結合程式碼詳細介紹下:
詳解:
FloatObject
懸浮的物體,繼承自View,需要重寫onDraw方法,主要作用就是來畫出自己,並進行隨機曲線運動。
任何需要畫出的物件都需要繼承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);
}
}
隨機曲線:
其實最複雜的部分就是讓漂浮的物體做隨機無規則的曲線運動,並且每個漂浮物的速度不同,這樣整個漂浮動畫才更加自然。
我之前想過使用布朗運動,但是在網上找了好久也沒找到一個好用的演算法。
最後只能還是使用3點賽貝爾曲線,使漂浮物沿著一條賽貝爾曲線運動,達到終點時,再隨機產生一條新的曲線,這樣就可以實現隨機曲線運動了。
控制運動的程式碼如下:
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模擬火花粒子的滑動噴射效果,如果大家有興趣可以看看。
FloatBackground
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”))
接收的三個引數分別為出生位置在螢幕寬的百分比,長的百分比,和顯示的文字。