美文网首页Android知识Android开发Android开发部落
Android 利用Surfaceview实现自定义位移动画

Android 利用Surfaceview实现自定义位移动画

作者: 齐小政 | 来源:发表于2016-12-15 20:28 被阅读3028次

    少说话,上图。。。
    看了效果图不高兴,就绕道了。。。哈哈

    2016-12-15 20_08_16.gif 2016-12-15 20_14_06.gif

    利用Surfaceview实现位移动画效果,可以根据具体需求,自己编写位移动画执行的算法。比如,电商APP中加入购物车的动画、特殊曲线的动画等等。

    Github地址:https://github.com/qizhenghao/AnimationSurfaceView

    以第一个抛物线的动画为例:
    在demoActivity中初始化动画:

     private void initParabolaAnimation() {
            animationSurfaceView = (AnimationSurfaceView) findViewById(R.id.animation_surfaceView);
            animationSurfaceView.setOnAnimationStausChangedListener(this);
            // 设置起始Y轴高度和终止X轴位移
            iAnimationStrategy = new ParabolaAnimationStrategy(animationSurfaceView, dp2px(320), dp2px(320));
            animationSurfaceView.setStrategy(iAnimationStrategy);
            animationSurfaceView.setIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
            animationSurfaceView.startAnimation();
        }
    

    AnimationSurfaceView 继承自 SurfaceView:

    package com.bruce.open.animationsurfaceview.lib;
    
    import android.content.Context;
    import android.graphics.*;
    import android.util.AttributeSet;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    /**
     * @author zhenghao.qi
     * @version 1.0
     * @time 2015年11月09日15:24:15
     */
    public class AnimationSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    
        private static final String TAG = "AnimationSurfaceView";
        private static final long REFRESH_INTERVAL_TIME = 15l;//每间隔15ms刷一帧
        private SurfaceHolder mSurfaceHolder;
        private Bitmap mBitmap;                               //动画图标
        private IAnimationStrategy mIAnimationStrategy;       //动画执行算法策略
        private OnStausChangedListener mStausChangedListener; //动画状态改变监听事件
    
        private int marginLeft;
        private int marginTop;
    
        private boolean isSurfaceDestoryed = true;            //默认未创建,相当于Destory
        private Thread mThread;                               //动画刷新线程
    
        public AnimationSurfaceView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
        }
    
        public AnimationSurfaceView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public AnimationSurfaceView(Context context) {
            super(context);
            init();
        }
    
        //初始化
        private void init() {
            mSurfaceHolder = getHolder();
            mSurfaceHolder.addCallback(this);
            setZOrderOnTop(true);//设置画布背景透明
            mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
            mThread = new Thread(this);
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            isSurfaceDestoryed = false;
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            isSurfaceDestoryed = true;
            if (mIAnimationStrategy != null)//如果surfaceView创建后,没有执行setStrategy,就被销毁,会空指针异常
                mIAnimationStrategy.cancel();
        }
    
        //执行
        private void executeAnimationStrategy() {
            Canvas canvas = null;
    
            Paint tempPaint = new Paint();
            tempPaint.setAntiAlias(true);
            tempPaint.setColor(Color.TRANSPARENT);
    
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setColor(Color.CYAN);
            if (mStausChangedListener != null) {
                mStausChangedListener.onAnimationStart(this);
            }
            mIAnimationStrategy.start();
            while (mIAnimationStrategy.doing()) {
                try {
                    mIAnimationStrategy.compute();
    
                    canvas = mSurfaceHolder.lockCanvas();
                    canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);// 设置画布的背景为透明
    
                    // 绘上新图区域
                    float x = (float) mIAnimationStrategy.getX() + marginLeft;
                    float y = (float) mIAnimationStrategy.getY() + marginTop;
    
                    canvas.drawRect(x, y, x + mBitmap.getWidth(), y + mBitmap.getHeight(), tempPaint);
                    canvas.drawBitmap(mBitmap, x, y, paint);
    
                    mSurfaceHolder.unlockCanvasAndPost(canvas);
                    Thread.sleep(REFRESH_INTERVAL_TIME);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            // clear屏幕内容
            if (isSurfaceDestoryed == false) {// 如果直接按Home键回到桌面,这时候SurfaceView已经被销毁了,lockCanvas会返回为null。
                canvas = mSurfaceHolder.lockCanvas();
                canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);
                mSurfaceHolder.unlockCanvasAndPost(canvas);
            }
    
            if (mStausChangedListener != null) {
                mStausChangedListener.onAnimationEnd(this);
            }
        }
    
        /**
         * 开始播放动画
         */
        public void startAnimation() {
            if (mThread.getState() == Thread.State.NEW) {
                mThread.start();
            } else if (mThread.getState() == Thread.State.TERMINATED) {
                mThread = new Thread(this);
                mThread.start();
            }
        }
    
        /**
         * 是否正在播放动画
         */
        public boolean isShow() {
            return mIAnimationStrategy.doing();
        }
    
        /**
         * 结束动画
         */
        public void endAnimation() {
            mIAnimationStrategy.cancel();
        }
    
        /**
         * 设置要播放动画的bitmap
         *
         * @param bitmap
         */
        public void setIcon(Bitmap bitmap) {
            this.mBitmap = bitmap;
        }
    
        /**
         * 获取要播放动画的bitmap
         */
        public Bitmap getIcon() {
            return mBitmap;
        }
    
        /**
         * 设置margin left 像素
         *
         * @param marginLeftPx
         */
        public void setMarginLeft(int marginLeftPx) {
            this.marginLeft = marginLeftPx;
        }
    
        /**
         * 设置margin left 像素
         *
         * @param marginTopPx
         */
        public void setMarginTop(int marginTopPx) {
            this.marginTop = marginTopPx;
        }
    
        /**
         * 设置动画状态改变监听器
         */
        public void setOnAnimationStausChangedListener(OnStausChangedListener listener) {
            this.mStausChangedListener = listener;
        }
    
        @Override
        public void run() {
            executeAnimationStrategy();
        }
    
        public interface OnStausChangedListener {
            void onAnimationStart(AnimationSurfaceView view);
    
            void onAnimationEnd(AnimationSurfaceView view);
        }
    
        /**
         * 设置动画执行算法策略
         *
         * @param strategy
         */
        public void setStrategy(IAnimationStrategy strategy) {
            this.mIAnimationStrategy = strategy;
        }
    
    }
    
    

    具体执行的动画算法策略:

    package com.bruce.open.animationsurfaceview.strategies;
    
    import android.util.Log;
    
    import com.bruce.open.animationsurfaceview.lib.AnimationSurfaceView;
    import com.bruce.open.animationsurfaceview.lib.IAnimationStrategy;
    
    /**
     * @author zhenghao.qi
     * @version 2015年11月10日10:40:03
     */
    public class ParabolaAnimationStrategy implements IAnimationStrategy {
        /**
         * 重力加速度值。
         */
        private static final float GRAVITY = 400.78033f;
        /**
         * 与X轴碰撞后,重力势能损失掉的百分比。
         */
        private static final float WASTAGE = 0.3f;
        /**
         * 起始下降高度。
         */
        private int height;
        /**
         * 起始点到终点的X轴位移。
         */
        private int width;
        /**
         * 水平位移速度。
         */
        private double velocity;
        /**
         * X Y坐标。
         */
        private double x, y;
        /**
         * 动画开始时间。
         */
        private long startTime;
        /**
         * 首阶段下载的时间。 单位:毫秒。
         */
        private double t1;
        /**
         * 第二阶段上升与下载的时间。 单位:毫秒。
         */
        private double t2;
        /**
         * 动画正在进行时值为true,反之为false。
         */
        private boolean doing;
    
        private AnimationSurfaceView animationSurfaceView;
    
        public ParabolaAnimationStrategy(AnimationSurfaceView animationSurfaceView, int h, int w) {
            this.animationSurfaceView = animationSurfaceView;
            setParams(h, w);
        }
    
        public void start() {
            startTime = System.currentTimeMillis();
            doing = true;
        }
    
        /**
         * 设置起始下落的高度及水平位移宽度;以此计算水平初速度、计算小球下落的第一阶段及第二阶段上升耗时。
         */
        private void setParams(int h, int w) {
            height = h;
            width = w;
    
            t1 = Math.sqrt(2 * height * 1.0d / GRAVITY);
            t2 = Math.sqrt((1 - WASTAGE) * 2 * height * 1.0d / GRAVITY);
            velocity = width * 1.0d / (t1 + 2 * t2);
            Log.d("Bruce1", "t1=" + t1 + " t2=" + t2);
        }
    
        /**
         * 根据当前时间计算小球的X/Y坐标。
         */
        public void compute() {
            double used = (System.currentTimeMillis() - startTime) * 1.0d / 1000;
            x = velocity * used;
            if (0 <= used && used < t1) {
                y = height - 0.5d * GRAVITY * used * used;
            } else if (t1 <= used && used < (t1 + t2)) {
                double tmp = t1 + t2 - used;
                y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;
            } else if ((t1 + t2) <= used && used < (t1 + 2 * t2)) {
                double tmp = used - t1 - t2;
                y = (1 - WASTAGE) * height - 0.5d * GRAVITY * tmp * tmp;
            } else {
                Log.d("Bruce1", "used:" + used + " set doing false");
                x = velocity * (t1 + 2 * t2);
                y = 0;
                doing = false;
            }
        }
    
        public double getX() {
            return x;
        }
    
        public double getY() {
            return getMirrorY(animationSurfaceView.getHeight(), animationSurfaceView.getIcon().getHeight());
        }
    
        /**
         * 反转Y轴正方向。适应手机的真实坐标系。
         */
        public double getMirrorY(int parentHeight, int bitHeight) {
            int half = parentHeight >> 1;
            double tmp = half + (half - y);
            tmp -= bitHeight;
            return tmp;
        }
    
        public boolean doing() {
            return doing;
        }
    
        public void cancel() {
            doing = false;
        }
    }
    
    

    这个小的demo是在15年时候写的了,如有错误。。。

    相关文章

      网友评论

        本文标题:Android 利用Surfaceview实现自定义位移动画

        本文链接:https://www.haomeiwen.com/subject/eomgmttx.html