首页 > 其他 > 详细

贝塞尔曲线实践-动画框架

时间:2017-02-09 13:58:26      阅读:302      评论:0      收藏:0      [点我收藏+]

前言

动画有多么重要,相信大家都清楚。它可以让一个枯燥乏味的静态界面变成一个充满动力的动画世界,提高用户体验。反正现在都是用户体验至上。android也是前端。苦逼的大前端。想想之前刚毕业的时候搞javaWeb,那个时候感觉前端好low。。现在不这么认为了。

废话不多少。直接上效果图

技术分享

主要就是中间那部分的动画效果。

理解Android中动画实现的本质

在理解Android中动画实现的本质之前,首先要理解动画实现的原理,估计这个大家都清楚。

如果要在Android中实现动画展示,那么就必须要有一个“动画驱动”每隔1/24秒去调用View的draw()方法,同时改变每一帧中View需要变化的元素,让这个View不断的绘制,这样一来,所有变化就是组合成一个流畅的动画。

上面就是“Android中动画实现的本质”,其关键就是要有一个“动画驱动”。回想下我们平时最常用的动画类Animation或者Animator,其实它们内部实现也是一个“动画驱动”,驱动View不断绘制。所以,我们完全可以不用Animation或者Animator去做动画,只要有一个“驱动”即可,例如Scroller是个不错的选择,甚至我们可以写一个我们自己实现的“动画驱动”。

常用的“动画驱动”

  1. View 本身

    view本身的onDraw()马上会触发下一次绘制。

    class MyView extends View {
        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            invalidate();
        }
    }
    
  2. View动画,属性动画(Animation/Animator)

    上面的部分就是使用属性动画.
    
  3. Scroller

    这个在刚开始的时候滑动主要就靠这个类。郭神的。医生的,还有爱哥的这些书中都有讲到。博客也有很多。它需要结合View的computeScroll()方法实现。

  4. 自己实现一个简易的“动画驱动”

    既然有些需求用原有的方法难以实现或者实现起来不太合适,这个时候我们就需要自己动手了。因此,我也写了一个简易的“动画驱动”

自定义动画驱动

其实就是自己把 path 的那些 moveTo ,lineTo 这些方法封装了下。勿喷。

  • PathPoint类

    /**
     * des:具体的路径集合
     * author: marc
     * date:  2017/2/8 11:31
     * email:aliali_ha@yeah.net
     */
    
    public class PathPoint {
        //移动指令
        public static final int MOVE = 0;
        //直线运动
        public static final int LINE = 1;
        //贝塞尔曲线
        public static final int CURVE = 2;
    
        //当前指令
        int mOperation;
        float mX;
        float mY;
        float mControl0X, mControl1X;//2个拐点
        float mControl0Y, mControl1Y;
    
        private PathPoint(int operation, float x, float y) {
            mOperation = operation;
            mX = x;
            mY = y;
        }
    
        private PathPoint(float c0x, float c0y, float c1x, float c1y, float x, float y) {
            mOperation = CURVE;
            //终点
            mX = x;
            mY = y;
            mControl0X = c0x;
            mControl0Y = c0y;
    
            mControl1X = c1x;
            mControl1Y = c1y;
        }
    
    
        /**
         * 移动
         *
         * @param x
         * @param y
         * @return
         */
        public static PathPoint moveTo(float x, float y) {
            return new PathPoint(MOVE, x, y);
        }
    
        /**
         * 直线
         *
         * @param x
         * @param y
         * @return
         */
        public static PathPoint lineTo(float x, float y) {
            return new PathPoint(LINE, x, y);
        }
    
        /**
         * 贝塞尔曲线
         *
         * @return
         */
        public static PathPoint curveTo(float c0x, float c0y, float c1x, float c1y, float x, float y) {
            return new PathPoint(c0x, c0y, c1x, c1y, x, y);
        }
    }
    
  • AnimatorPath类

    /**
     * des:动画路径
     * author: marc
     * date:  2017/2/8 11:26
     * email:aliali_ha@yeah.net
     */
    
    public class AnimatorPath {
    
        //存储路径集合
        ArrayList<PathPoint> mPoints = new ArrayList<>();
    
        /**
         * 移动到哪个位置
         *
         * @param x
         * @param y
         */
        public void moveTo(float x, float y) {
            mPoints.add(PathPoint.moveTo(x, y));
        }
    
        //直线
        public void lineTo(float x, float y) {
            mPoints.add(PathPoint.lineTo(x, y));
        }
    
        //贝塞尔曲线
        public void curveTo(float c0x, float c0y, float c1x, float c1y, float x, float y) {
            mPoints.add(PathPoint.curveTo(c0x, c0y, c1x, c1y, x, y));
        }
    
        public Collection<PathPoint> getPoints() {
            return mPoints;
        }
    
        public void clear() {
            if (mPoints != null && mPoints.size() > 0) {
                mPoints.clear();
            }
        }
    }
    
  • PathEvaluator类 主要实现了move line 贝塞尔曲线这些方法

    /**
     * des:自定义估值器
     * author: marc
     * date:  2017/2/8 13:06
     * email:aliali_ha@yeah.net
     */
    
    public class PathEvaluator implements TypeEvaluator<PathPoint> {
    
        /**
         * @param t          动画执行的百分比 ,其实就是时间
         * @param startValue
         * @param endValue
         * @return
         */
        @Override
        public PathPoint evaluate(float t, PathPoint startValue, PathPoint endValue) {
            //进行估值
            float x, y;
            //判断进行哪种运动
            if (endValue.mOperation == PathPoint.CURVE) {
                //贝塞尔曲线方式
                float oneMinusT = 1 - t;
                //x的实时坐标
                x = oneMinusT * oneMinusT * oneMinusT * startValue.mX +
                        3 * oneMinusT * oneMinusT * t * endValue.mControl0X +
                        3 * oneMinusT * t * t * endValue.mControl1X +
                        t * t * t * endValue.mX;
                //y的实时坐标
                y = oneMinusT * oneMinusT * oneMinusT * startValue.mY +
                        3 * oneMinusT * oneMinusT * t * endValue.mControl0Y +
                        3 * oneMinusT * t * t * endValue.mControl1Y +
                        t * t * t * endValue.mY;
            } else if (endValue.mOperation == PathPoint.LINE) {
                //直线运动方式
                //当前坐标点(x,y) = 起始点 +t*起始点和终点的距离
                x = startValue.mX + t * (endValue.mX - startValue.mX);
                y = startValue.mY + t * (endValue.mY - startValue.mY);
            } else {
                //moveto方式
                x = endValue.mX;
                y = endValue.mY;
            }
            //不断的把控制点 move到 x,y的位置
            return PathPoint.moveTo(x, y);
        }
    }
    
  • Activity类

    /**
     * des:
     * author: marc
     * date:  2017/2/8 09:53
     * email:aliali_ha@yeah.net
     */
    public class BezierActivity extends AppCompatActivity {
        private static final long ANIMATION_DUARTION = 400;//运动时间
        private static final float MINIMUN_X_DISTANCE = 200;//x轴运动距离
        private static final float SCALE_FACTOR_EXPAND = 13;//扩大倍数
        private static final float SCALE_FACTOR_ORI = 1;//最初的倍数
        ImageButton mFab;
        FrameLayout mFabContainer;//帧布局
        LinearLayout mControlsContainer;
        private int mFabSize;//ImageButton的大小
        private boolean mRevealFlag;
        private boolean mResetFlag;
        private float startX;
        private float startY;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_bezier);
            mFabSize = getResources().getDimensionPixelSize(R.dimen.fab_size);
            bindViews();
        }
    
        private void bindViews() {
            mFab = (ImageButton) findViewById(R.id.fab);
            mFab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onFabPressed(v);
                }
            });
            mFabContainer = (FrameLayout) findViewById(R.id.fab_container);
            mControlsContainer = (LinearLayout) findViewById(R.id.media_controls_container);
        }
    
        public void onFabPressed(View view) {
            //还没运动前的X坐标
            startX = mFab.getX();
            startY = mFab.getY();
            //开启动画 使用属性动画   属性动画控制对象身上的任何属性值 (必须有set方法)
    
            AnimatorPath mPath = new AnimatorPath();
            mPath.moveTo(0, 0);
            //贝塞尔曲线
            mPath.curveTo(-200, 200, -400, 100, -600, 0);
            //相对于原来移动的点
            //填this是控制当前对象(当前是BezierActivity)的PathPoint p 这个属性 。 mPath.getPoints()是相当于从某个值到某个值
            ObjectAnimator animator = ObjectAnimator.ofObject(this, "fabLocation", new PathEvaluator(), mPath.getPoints().toArray());
            animator.setDuration(ANIMATION_DUARTION);
            //设置加速
            animator.start();
    
    
            //水波纹效果
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //X轴运动距离超过200  开始水波纹扩散
                    if (Math.abs(startX - mFab.getX()) > MINIMUN_X_DISTANCE) {
                        if (!mRevealFlag) {
                            //高版本 api 运动
                            //已经设置成透明的了。设置回来
                            mFab.setImageDrawable(new BitmapDrawable());
                            mFabContainer.setY(mFabContainer.getY() + mFabSize / 2);
                            mFab.animate()
                                    .scaleX(SCALE_FACTOR_EXPAND)
                                    .scaleY(SCALE_FACTOR_EXPAND)
                                    .setListener(endListener)
                                    .setDuration(ANIMATION_DUARTION)
                                    .start();
                            mRevealFlag = true;
                            mResetFlag = false;
                        }
    
                    }
                }
            });
        }
    
        //反射  不用直接设置属性
        PathPoint fabLocation;
    
        public void setFabLocation(PathPoint fabLocation) {
            //达到不断的控制view进行移动
            mFab.setTranslationX(fabLocation.mX);
            mFab.setTranslationY(fabLocation.mY);
        }
    
    
        //开始动画。效果跟点击开始按钮一样
        public void startAnimator(View view) {
            onFabPressed(view);
        }
    
        /**
         * fab做正向移动时的listerner
         */
        private AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
            }
    
            @Override
            public void onAnimationEnd(Animator animation) {
                mFab.setVisibility(View.INVISIBLE);
                mFabContainer.setBackgroundColor(getResources().getColor(R.color.brand_accent));
                for (int i = 0; i < mFabContainer.getChildCount(); i++) {
                    View v = mControlsContainer.getChildAt(i);
                    ViewPropertyAnimator animator = v.animate().scaleX(1).scaleY(1).setDuration(ANIMATION_DUARTION);
                    //依次显示
                    animator.setStartDelay(i * 50).start();
                }
            }
    
        };
    
        /**
         * 重置动画  1.水波纹
         * 2. framelayout布局上移
         * 3. fab做位移
         *
         * @param view
         */
        public void reset(View view) {
            //1.先隐藏
            for (int i = 0; i < mFabContainer.getChildCount() + 1; i++) {
                View v = mControlsContainer.getChildAt(i);
                ViewPropertyAnimator animator = v.animate().scaleX(0).scaleY(0).setDuration(ANIMATION_DUARTION);
                //依次显示
                animator.setStartDelay(i * 50);
                //最后一个动画的时候监听动画结束 ,开始显示fab。然后进行缩放
                if (i == mControlsContainer.getChildCount() - 1) {
                    animator.setListener(reverseListener);
                }
                animator.start();
            }
    
            AnimatorPath mPath1 = new AnimatorPath();
            mPath1.moveTo(-600, 0);
            mPath1.lineTo(0, 0);
            //相对于原来移动的点
            //填this是控制当前对象(当前是BezierActivity)的PathPoint p 这个属性 。 mPath.getPoints()是相当于从某个值到某个值
            ObjectAnimator fabLocation = ObjectAnimator.ofObject(this, "fabLocation", new PathEvaluator(), mPath1.getPoints().toArray());
            fabLocation.setDuration(ANIMATION_DUARTION);
            //设置加速
            fabLocation.start();
    
    
        }
    
    
        /**
         * 2.结束的时候fab显示。然后进行缩放
         */
        private AnimatorListenerAdapter reverseListener = new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                //结束的时候背景显示成透明
                mFabContainer.setBackgroundColor(getResources().getColor(android.R.color.transparent));
                //ImageButton设置成显示状态
                mFab.setVisibility(View.VISIBLE);
                //2. ImageButton开始缩放 从13-1的缩放
                PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 13, 1);
                PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 13, 1);
                ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mFab, scaleX, scaleY);
                objectAnimator.setDuration(ANIMATION_DUARTION);
                objectAnimator.addListener(listenerAdapter);
                objectAnimator.start();
            }
        };
    
    
        private AnimatorListenerAdapter listenerAdapter = new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
            }
    
            @Override
            public void onAnimationEnd(Animator animation) {
                //ImageButton缩放动画结束的时候
                if (!mResetFlag) {
                    mFabContainer.setY(mFabContainer.getY() - mFabSize / 2);
                    mFab.setImageBitmap(BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_media_pause));
                    mResetFlag = true;
                    mRevealFlag = false;
                }
            }
        };
    }
    

同时上传到github

地址: https://github.com/Xiemarc/DesignPatterns

贝塞尔曲线实践-动画框架

原文:http://blog.csdn.net/u012721933/article/details/54933498

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!