Android实现的一个点赞动画

摘要

使用Canvas和状态模式实现一个点赞效果

点赞动画效果

关键步骤

1. 获取Bitmap对象和Bitmap对象释放。

创建

BitmapFactory.decodeResource(getResources(), R.drawable.encourage_gif_big);

回收

if(bitmap != null && !bitmap.isRecycled()){   
        bitmap.recycle();   
        bitmap = null;   
}   
System.gc();  

如果图片过大可以压缩

//压缩,用于节省BITMAP内存空间--解决BUG的关键步骤    
BitmapFactory.Options opts = new BitmapFactory.Options();   
opts.inSampleSize = 2;    //这个的值压缩的倍数(2的整数倍),数值越小,压缩率越小,图片越清晰    
   
//返回原图解码之后的bitmap对象    
bitmap = BitmapFactory.decodeResource(Context, ResourcesId, opts); 

2. 创建画笔,设置画笔。

mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
mBitmapPaint.setAlpha(255);

3. canvas绘制Bitmap

float scale = 1 - (1f - END_SCALE) / TOTAL_TIME * currentTime;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale, mWidth / 2, mHeight / 2);
canvas.drawBitmap(mLikesBitmap, matrix, mBitmapPaint);

4. 分析动画状态,分解状态

动画状态

将动画过程分解成几个阶段,然后定义基类(模板方法)

5. 计算各个状态的变化过程,用总时间等分绘制。

float scale = 1 - (1f - END_SCALE) / TOTAL_TIME * currentTime;

关键点

  1. 简单自定义View实现

重写onDraw()

  1. 状态模式的使用

分解状态,将复杂问题拆分解决的思路。

代码

package lib.ui.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;

import com.renxing.xys.R;

/**
 * Created by 水寒 on 2018/2/5.
 * 点赞效果
 */

public class LikesAnimView extends View{

    protected static final int DRAW_GAP_TIME = 50;
    private static final int HAND_INVALI = 0x0001;

    private Status mStatus;


    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case HAND_INVALI:
                    postInvalidate();
                    break;
            }
        }
    };

    private Bitmap mLikesBitmap;
    private Bitmap mStarsBitmap;
    private Paint mBitmapPaint;

    private int mWidth;
    private int mHeight;


    public LikesAnimView(Context context) {
        super(context);
        init();
    }

    public LikesAnimView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LikesAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LikesAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init(){
        mLikesBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.encourage_gif_big);
        mStarsBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.encourage_gif_big_1);
        mBitmapPaint = new Paint();
        mBitmapPaint.setAntiAlias(true);
        mStatus = new EndStatu();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
    }

    /**
     * 开始动画
     */
    public void startAnim(){
        mStatus = new LikeToBigStatu();
        mHandler.sendEmptyMessage(HAND_INVALI);
    }

    /**
     * 结束动画
     */
    public void stopAnim(){
        mStatus = new EndStatu();
        mHandler.removeMessages(HAND_INVALI);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mStatus.draw(canvas);
    }

    abstract class Status{

        protected static final float SMALL_LIKE_SCALE = 0.7f;

        public Status(){
            mBitmapPaint.setAlpha(255);
        }
        
        abstract void toNext();

        abstract void draw(Canvas canvas);

    }

    class LikeToBigStatu extends Status{

        private static final int TOTAL_TIME = 200;
        private static final float START_SCALE = SMALL_LIKE_SCALE;

        int currentTime = 0;

        @Override
        void toNext() {
            mStatus = new LikeToSmallStatu();
        }

        @Override
        void draw(Canvas canvas) {
            float scale = START_SCALE + (1f - START_SCALE) / TOTAL_TIME * currentTime;
            Matrix matrix = new Matrix();
            matrix.postScale(scale, scale, mWidth / 2, mHeight / 2);
            canvas.drawBitmap(mLikesBitmap, matrix, mBitmapPaint);
            mHandler.sendEmptyMessageDelayed(HAND_INVALI, DRAW_GAP_TIME);
            currentTime = currentTime + DRAW_GAP_TIME;
            if(currentTime >= TOTAL_TIME) toNext();
        }
    }

    class LikeToSmallStatu extends Status{

        private static final int TOTAL_TIME = 200;
        private static final float END_SCALE = SMALL_LIKE_SCALE;

        int currentTime = 0;

        @Override
        void toNext() {
            mStatus = new StarToBigStatu();
        }

        @Override
        void draw(Canvas canvas) {
            float scale = 1 - (1f - END_SCALE) / TOTAL_TIME * currentTime;
            Matrix matrix = new Matrix();
            matrix.postScale(scale, scale, mWidth / 2, mHeight / 2);
            canvas.drawBitmap(mLikesBitmap, matrix, mBitmapPaint);
            mHandler.sendEmptyMessageDelayed(HAND_INVALI, DRAW_GAP_TIME);
            currentTime = currentTime + DRAW_GAP_TIME;
            if(currentTime >= TOTAL_TIME) toNext();
        }
    }

    class StarToBigStatu extends Status{

        private static final int TOTAL_TIME = 200;
        private static final float START_SCALE = 0.6f;

        int currentTime = 0;

        @Override
        void toNext() {
            mStatus = new StarOutStatu();
        }

        @Override
        void draw(Canvas canvas) {
            float scale = START_SCALE + (1f - START_SCALE) / TOTAL_TIME * currentTime;
            Matrix matrix1 = new Matrix();
            matrix1.postScale(scale, scale, mWidth / 2, mHeight / 2);
            canvas.drawBitmap(mStarsBitmap, matrix1, mBitmapPaint);
            Matrix matrix2 = new Matrix();
            matrix2.postScale(SMALL_LIKE_SCALE, SMALL_LIKE_SCALE, mWidth / 2, mHeight / 2);
            canvas.drawBitmap(mLikesBitmap, matrix2, mBitmapPaint);
            mHandler.sendEmptyMessageDelayed(HAND_INVALI, DRAW_GAP_TIME);
            currentTime = currentTime + DRAW_GAP_TIME;
            if(currentTime >= TOTAL_TIME) toNext();
        }
    }
    
    class StarOutStatu extends Status{

        private static final int TOTAL_TIME = 200;
        private static final float END_ALPHA = 0f;

        int currentTime = 0;

        @Override
        void toNext() {
            mStatus = new LikeOutStatu();
        }

        @Override
        void draw(Canvas canvas) {
            float alpha = 255 - (255f - END_ALPHA) / TOTAL_TIME * currentTime;
            mBitmapPaint.setAlpha((int)alpha);
            Matrix matrix1 = new Matrix();
            matrix1.postScale(1, 1);
            canvas.drawBitmap(mStarsBitmap, matrix1, mBitmapPaint);
            Matrix matrix2 = new Matrix();
            matrix2.postScale(SMALL_LIKE_SCALE, SMALL_LIKE_SCALE, mWidth / 2, mHeight / 2);
            mBitmapPaint.setAlpha(255);
            canvas.drawBitmap(mLikesBitmap, matrix2, mBitmapPaint);
            mHandler.sendEmptyMessageDelayed(HAND_INVALI, DRAW_GAP_TIME);
            currentTime = currentTime + DRAW_GAP_TIME;
            if(currentTime >= TOTAL_TIME) toNext();
        }
    }
    
    class LikeOutStatu extends Status{

        private static final int TOTAL_TIME = 200;
        private static final float END_ALPHA = 0f;

        int currentTime = 0;

        @Override
        void toNext() {
            mStatus = new EndStatu();
        }

        @Override
        void draw(Canvas canvas) {
            float alpha = 255 - (255f - END_ALPHA) / TOTAL_TIME * currentTime;
            mBitmapPaint.setAlpha((int)alpha);
            Matrix matrix = new Matrix();
            matrix.postScale(SMALL_LIKE_SCALE, SMALL_LIKE_SCALE, mWidth / 2, mHeight / 2);
            canvas.drawBitmap(mLikesBitmap, matrix, mBitmapPaint);
            mHandler.sendEmptyMessageDelayed(HAND_INVALI, DRAW_GAP_TIME);
            currentTime = currentTime + DRAW_GAP_TIME;
            if(currentTime >= TOTAL_TIME) toNext();
        }
    }

    class EndStatu extends Status{

        @Override
        void toNext() {

        }

        @Override
        void draw(Canvas canvas) {

        }
    }
}

代码中注释比较少,但是整个结构脉络很清晰,将动画非为好几个阶段去重写draw()方法实现具体绘制,toNext()方法实现交接(状态交接),demo虽然小,但是这种思路可以完成一个更为复杂的动画绘制过程。