前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简单实现满屏表情下落的动画效果,你也可以

简单实现满屏表情下落的动画效果,你也可以

作者头像
俞其荣
发布2022-07-28 08:02:26
7800
发布2022-07-28 08:02:26
举报
文章被收录于专栏:俞其荣的博客

首先我相信大家一定玩过微信吧。之前在玩微信的时候,给好友发一句“圣诞快乐”就会有满屏的圣诞树往下掉,当时觉得这个动画好酷。正好在公司的项目中需要用到这样的动画效果。于是写了一个小Demo,就有了这篇文章。

下图是做出的相关效果:

表情下落动画效果gif

看完上面的效果图,大家一定都迫不及待地想要试一试了,那就让我们来动手吧。

首先我们定义一个实体类DropLook:

代码语言:javascript
复制
/**
 * 下落的表情
 */
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对象。

代码语言:javascript
复制
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():

代码语言:javascript
复制
@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():

代码语言:javascript
复制
@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的完整代码:

代码语言:javascript
复制
/**
 * 表情下落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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档