博文出处:简单实现满屏表情下落的动画效果,你也可以,欢迎大家关注我的博客,谢谢!
首先我相信大家一定玩过微信吧。之前在玩微信的时候,给好友发一句“圣诞快乐”就会有满屏的圣诞树往下掉,当时觉得这个动画好酷。正好在公司的项目中需要用到这样的动画效果。于是写了一个小Demo,就有了这篇文章。
下图是做出的相关效果:
看完上面的效果图,大家一定都迫不及待地想要试一试了,那就让我们来动手吧。
首先我们定义一个实体类DropLook:
/**
* 下落的表情
*/
public class DropLook {
// x轴坐标
private float x;
// y轴坐标
private float y;
// 初始旋转角度
private float rotation;
// 下落速度
private float speed;
// 旋转速度
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;
}
}
我们定义的实体类很简单,只是设置了如宽高、x,y坐标、下落速度等。接下来我们再创建一个DropLookFactory类,用来创建DropLook对象。
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");
}
// 设置与图片等宽
look.setWidth(originalBitmap.getWidth());
// 设置与图片等高
look.setHeight(originalBitmap.getHeight());
// 设置起始位置的X坐标
look.setX((float) Math.random() * (width - look.getWidth()));
// 设置起始位置的Y坐标
look.setY((float) Math.random() * (height - look.getHeight()));
// 设置速度
look.setSpeed(20 + (float) Math.random() * 40);
// 设置初始旋转角度
look.setRotation((float) Math.random() * 180 - 90);
// 设置旋转速度
look.setRotationSpeed((float) Math.random() * 90 - 60);
// 设置图片
look.setBitmap(originalBitmap);
return look;
}
}
其中createDropLook(Context context, float xRange, Bitmap originalBitmap)
的第一个参数代表着下落表情在x轴上的范围,第二个参数代表在y轴上的范围,第三个参数是表情的图片。在createDropLook方法中相信大家都看得懂,主要就是用随机数初始化DropLook的坐标及下落速度等。
好了,下面就是今天的重头戏DropLookView,先来看看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里主要是对View的测量,如果是wrap_content
的话设置一个默认的宽高度200dp。然后就是初始化DropLook,looks是DropLook类的集合,用于管理DropLook。而DEFAULT_LOOK_NUMS
是默认的looks集合的数量。
接下来就是最关键的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();
}
一开始判断时间间隔如果没有超过100ms,就让线程睡眠一会。然后就是用drawBitmap的方法把looks里面逐个绘制出来。并且再把look的y轴坐标加上下落速度等,旋转的角度也是如此。最后就是调用invalidate()不断地重绘。总体上并没有什么难点。
以下是DropLookView的完整代码:
/**
* 表情下落view
*/
public class DropLookView extends View {
// 表情
private Bitmap mBitmap;
// 所有表情集合
List<DropLook> looks = new ArrayList();
// view开始时间
private long startTime;
// view宽度
private int mWidth;
// view高度
private int mHeight;
// 画笔
private Paint mPaint;
// 默认表情下落数
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();
}
}
该讲的也差不多讲完了,其实并没有想象中的那么有难度,实现起来也比较容易。当然DropLookView也有需要改进的地方。比如说可以在布局文件中自定义表情下落的数量等。这些就需要自己根据需求来更改了,那今天就先这样吧。
下面是本Demo的完整代码:
DropLookView.rar