博文出處:簡(jiǎn)單實(shí)現(xiàn)滿屏表情下落的動(dòng)畫效果抵碟,你也可以桃漾,歡迎大家關(guān)注我的博客,謝謝拟逮!
首先我相信大家一定玩過(guò)微信吧撬统。之前在玩微信的時(shí)候,給好友發(fā)一句“圣誕快樂(lè)”就會(huì)有滿屏的圣誕樹往下掉敦迄,當(dāng)時(shí)覺(jué)得這個(gè)動(dòng)畫好酷恋追。正好在公司的項(xiàng)目中需要用到這樣的動(dòng)畫效果。于是寫了一個(gè)小Demo,就有了這篇文章罚屋。
下圖是做出的相關(guān)效果:
看完上面的效果圖苦囱,大家一定都迫不及待地想要試一試了,那就讓我們來(lái)動(dòng)手吧脾猛。
首先我們定義一個(gè)實(shí)體類DropLook:
/**
* 下落的表情
*/
public class DropLook {
// x軸坐標(biāo)
private float x;
// y軸坐標(biāo)
private float y;
// 初始旋轉(zhuǎn)角度
private float rotation;
// 下落速度
private float speed;
// 旋轉(zhuǎn)速度
private float rotationSpeed;
// 寬度
private int width;
// 高度
private int height;
// 圖片
private Bitmap bitmap;
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getRotationSpeed() {
return rotationSpeed;
}
public void setRotationSpeed(float rotationSpeed) {
this.rotationSpeed = rotationSpeed;
}
public float getRotation() {
return rotation;
}
public void setRotation(float rotation) {
this.rotation = rotation;
}
public float getSpeed() {
return speed;
}
public void setSpeed(float speed) {
this.speed = speed;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
我們定義的實(shí)體類很簡(jiǎn)單撕彤,只是設(shè)置了如寬高、x猛拴,y坐標(biāo)羹铅、下落速度等。接下來(lái)我們?cè)賱?chuàng)建一個(gè)DropLookFactory類愉昆,用來(lái)創(chuàng)建DropLook對(duì)象职员。
public class DropLookFactory {
private DropLookFactory() {
}
public static DropLook createDropLook(int width, int height,Bitmap originalBitmap) {
DropLook look = new DropLook();
if (originalBitmap == null) {
throw new NullPointerException("originalBitmap cannot be null");
}
// 設(shè)置與圖片等寬
look.setWidth(originalBitmap.getWidth());
// 設(shè)置與圖片等高
look.setHeight(originalBitmap.getHeight());
// 設(shè)置起始位置的X坐標(biāo)
look.setX((float) Math.random() * (width - look.getWidth()));
// 設(shè)置起始位置的Y坐標(biāo)
look.setY((float) Math.random() * (height - look.getHeight()));
// 設(shè)置速度
look.setSpeed(20 + (float) Math.random() * 40);
// 設(shè)置初始旋轉(zhuǎn)角度
look.setRotation((float) Math.random() * 180 - 90);
// 設(shè)置旋轉(zhuǎn)速度
look.setRotationSpeed((float) Math.random() * 90 - 60);
// 設(shè)置圖片
look.setBitmap(originalBitmap);
return look;
}
}
其中createDropLook(Context context, float xRange, Bitmap originalBitmap)
的第一個(gè)參數(shù)代表著下落表情在x軸上的范圍,第二個(gè)參數(shù)代表在y軸上的范圍跛溉,第三個(gè)參數(shù)是表情的圖片焊切。在createDropLook方法中相信大家都看得懂,主要就是用隨機(jī)數(shù)初始化DropLook的坐標(biāo)及下落速度等倒谷。
好了蛛蒙,下面就是今天的重頭戲DropLookView糙箍,先來(lái)看看onMeasure():
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mWidth = widthSize;
} else {
mWidth = Tools.dip2px(getContext(),200);
}
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize;
} else {
mHeight = Tools.dip2px(getContext(),200);
}
setMeasuredDimension(mWidth, mHeight);
if (looks.size() == 0) {
for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; ++i) {
looks.add(DropLookFactory.createDropLook(mWidth, mHeight, mBitmap));
}
Log.i(TAG, "num = " + looks.size());
}
}
onMeasure里主要是對(duì)View的測(cè)量渤愁,如果是wrap_content
的話設(shè)置一個(gè)默認(rèn)的寬高度200dp。然后就是初始化DropLook深夯,looks是DropLook類的集合抖格,用于管理DropLook诺苹。而DEFAULT_LOOK_NUMS
是默認(rèn)的looks集合的數(shù)量。
接下來(lái)就是最關(guān)鍵的onDraw():
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
long nowTime = System.currentTimeMillis();
if (nowTime - startTime < 100) {
try {
Thread.sleep(100 + startTime - nowTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; i++) {
DropLook look = looks.get(i);
mMatrix.setTranslate(-look.getWidth() / 2, -look.getHeight() / 2);
mMatrix.postRotate(look.getRotation());
mMatrix.postTranslate(look.getWidth() / 2 + look.getX(), look.getHeight() / 2 + look.getY());
canvas.drawBitmap(look.getBitmap(), mMatrix, mPaint);
look.setY(look.getY() + look.getSpeed());
if (look.getY() > getHeight()) {
look.setY((float) (0 - Math.random() * look.getHeight()));
}
look.setRotation(look.getRotation() + look.getRotationSpeed());
}
canvas.restore();
startTime = System.currentTimeMillis();
invalidate();
}
一開始判斷時(shí)間間隔如果沒(méi)有超過(guò)100ms雹拄,就讓線程睡眠一會(huì)收奔。然后就是用drawBitmap的方法把looks里面逐個(gè)繪制出來(lái)。并且再把look的y軸坐標(biāo)加上下落速度等滓玖,旋轉(zhuǎn)的角度也是如此坪哄。最后就是調(diào)用invalidate()不斷地重繪∈拼郏總體上并沒(méi)有什么難點(diǎn)翩肌。
以下是DropLookView的完整代碼:
/**
* 表情下落view
*/
public class DropLookView extends View {
// 表情
private Bitmap mBitmap;
// 所有表情集合
List<DropLook> looks = new ArrayList();
// view開始時(shí)間
private long startTime;
// view寬度
private int mWidth;
// view高度
private int mHeight;
// 畫筆
private Paint mPaint;
// 默認(rèn)表情下落數(shù)
private static final int DEFAULT_DROP_LOOK_NUMS = 35;
private static final String TAG = "DropLookView";
private Matrix mMatrix = new Matrix();
public DropLookView(Context context) {
this(context, null);
}
public DropLookView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DropLookView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 圖片
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.d_5_xiaoku);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mWidth = widthSize;
} else {
mWidth = Tools.dip2px(getContext(),200);
}
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize;
} else {
mHeight = Tools.dip2px(getContext(),200);
}
setMeasuredDimension(mWidth, mHeight);
if (looks.size() == 0) {
for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; ++i) {
looks.add(DropLookFactory.createDropLook(mWidth, mHeight, mBitmap));
}
Log.i(TAG, "num = " + looks.size());
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
long nowTime = System.currentTimeMillis();
if (nowTime - startTime < 100) {
try {
Thread.sleep(100 + startTime - nowTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; i++) {
DropLook look = looks.get(i);
mMatrix.setTranslate(-look.getWidth() / 2, -look.getHeight() / 2);
mMatrix.postRotate(look.getRotation());
mMatrix.postTranslate(look.getWidth() / 2 + look.getX(), look.getHeight() / 2 + look.getY());
canvas.drawBitmap(look.getBitmap(), mMatrix, mPaint);
look.setY(look.getY() + look.getSpeed());
if (look.getY() > getHeight()) {
look.setY((float) (0 - Math.random() * look.getHeight()));
}
look.setRotation(look.getRotation() + look.getRotationSpeed());
}
canvas.restore();
startTime = System.currentTimeMillis();
invalidate();
}
}
該講的也差不多講完了,其實(shí)并沒(méi)有想象中的那么有難度禁悠,實(shí)現(xiàn)起來(lái)也比較容易念祭。當(dāng)然DropLookView也有需要改進(jìn)的地方。比如說(shuō)可以在布局文件中自定義表情下落的數(shù)量等碍侦。這些就需要自己根據(jù)需求來(lái)更改了粱坤,那今天就先這樣吧。
下面是本Demo的完整代碼:
DropLookView.rar