Android——自定义View(三)

2019-09-29  本文已影响0人  如愿以偿丶

1.概述

  最近看到 红橙Darren 写的贝塞尔曲线 - 花束直播点赞效果后实现,来记录一下自己实现的步骤。

2.效果

              在这里插入图片描述

3.实现思路:

  1.点击添加按钮的时候有一张图片
  2.延S型曲线从底部向上运动

在这里插入图片描述

4.开始

  4.1.自定义RelativeLayout,初始化一些基本的参数

    public class LoveLayout extends RelativeLayout {
        //随机数
        private Random mRandom;
        //设置资源
        private int[] mImageRes;
        //屏幕宽高
        private int mWidth,mHeight;
        //图片的宽高
        private int mResWidth,mResheight;
        //插值器集合
        private Interpolator[] mInterpolator;
    
        public LoveLayout(Context context) {
            this(context,null);
        }
    
        public LoveLayout(Context context, AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            //1.初始化随机数
            mRandom = new Random();
            //2.初始化资源图片
            mImageRes = new int[]{R.drawable.green,R.drawable.red,R.drawable.note};
            //3.获取图片的宽高
            Drawable drawable = getContext().getResources().getDrawable(R.drawable.green);
            mResheight = drawable.getIntrinsicHeight();
            mResWidth = drawable.getIntrinsicWidth();
            //4.插值器集合
    //        mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator()
    //                                            ,new AccelerateInterpolator()
    //                                            ,new DecelerateInterpolator()
    //                                            ,new LinearInterpolator()};
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
}

  4.2.点击添加按钮,不断的往RelativeLayout中添加随机的ImageView,然后开始执行缩放和透明度的动画,采用属性动画ObjectAnimator.ofFloat();

    public void addView() {
         //创建ImageView,设置图片资源
         final ImageView ivLove = new ImageView(getContext());
         //设置图片资源随机
         ivLove.setImageResource(mImageRes[mRandom.nextInt(mImageRes.length)]);
         //设置图片添加位置
         RelativeLayout.LayoutParams params =  new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
         params.addRule(ALIGN_PARENT_BOTTOM);
         params.addRule(CENTER_HORIZONTAL);
         ivLove.setLayoutParams(params);
         //添加到父容器
         addView(ivLove);
    
         //添加属性动画  缩放和透明度效果
         AnimatorSet set = getAnimator(ivLove);
         //执行动画
         set.start();
      }
    
    private AnimatorSet getAnimator(ImageView ivLove) {
         //1.设置动画,透明度,缩放
         AnimatorSet innerAnimator = new AnimatorSet();
         ValueAnimator alphaAnimator = ObjectAnimator.ofFloat(ivLove,"alpha",0.5f,1f);
         ValueAnimator scaleXAnimator = ObjectAnimator.ofFloat(ivLove,"scaleX",0.5f,1f);
         ValueAnimator scaleYAnimator = ObjectAnimator.ofFloat(ivLove,"scaleY",0.5f,1f);
         innerAnimator.setDuration(350);
         //一起执行
         innerAnimator.playTogether(alphaAnimator,scaleXAnimator,scaleYAnimator);
         return innerAnimator ;
        }    
    }

  4.3.将图片延S型曲线进行运动(使用贝塞尔曲线 + 属性动画 + 自定义TypeEvaluator)

先看一下公式

在这里插入图片描述
    /**
     * 贝塞尔路径动画  自定义TypeEvaluator
     * @return
     */
    private Animator getBezierAnimator(final ImageView ivLove) {
        //1.四个点 p0,p1,p2,p3
        PointF point0 = new PointF(mWidth / 2 - mResWidth / 2,mHeight - mResheight) ;
        //2.P2点的y坐标要大于p1点的y坐标
        PointF point1 = getPoint(1);
        PointF point2 = getPoint(2);
        //终点为屏幕的一半
        Log.e("getBezierAnimator",mHeight+"");
        PointF point3 = new PointF(mRandom.nextInt(mWidth - mResWidth),0);

        //构造方法中传入中间的点
        LoveTypeEvaluator typeEvaluator = new LoveTypeEvaluator(point1,point2);
        ValueAnimator bezierAnimator = ObjectAnimator.ofObject(typeEvaluator,point0,point3);
//        bezierAnimator.setInterpolator(mInterpolator[mRandom.nextInt(mInterpolator.length -1)]);
        bezierAnimator.setDuration(6000);
        bezierAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //获取当前的值
                PointF pointF = (PointF) animation.getAnimatedValue();
                //设置位置
                ivLove.setX(pointF.x);
                ivLove.setY(pointF.y);
                //获取的是自定义TypeEvaluator,evaluate方法中的t值,该值是0-1的
                float alpha = animation.getAnimatedFraction(); //他是0-1的数
                ivLove.setAlpha(1- alpha + 0.2f);
            }
        });

        return bezierAnimator;
    }

    /**
     * 1代表p1  2代表p2
     * p1的时候y最大值位 mHeight / 2
     * p2的时候y最大值 mHeight / 2 + ((2-1) *mHeight / 2) = mHeight
     * @param index
     * @return
     */
    private PointF getPoint(int index) {
        return new PointF(mRandom.nextInt(mWidth - mResWidth),mRandom.nextInt(mHeight / 2 +(index-1)*mHeight/2));
    }
    //自定义TypeEvaluator
    public class LoveTypeEvaluator implements TypeEvaluator<PointF> {
        //中间点p1,p2,实现TypeEvaluator实现的evaluate方法只有起始点和终点,所以中间点需要我们通过构造方法传入
        private PointF point1;
        private PointF point2;
    
        public LoveTypeEvaluator(PointF point1, PointF point2) {
            this.point1 = point1;
            this.point2 = point2;
        }
    
        /**
         *
         * @param t [0 ,1]
         * @param point0    起始点
         * @param point3    终点
         * @return
         */
        @Override
        public PointF evaluate(float t, PointF point0, PointF point3) {
            PointF pointF = new PointF();
            //直接套上方的公式,三阶贝塞尔曲线
            pointF.x = point0.x * (1-t) * (1-t) * (1-t)
                        + 3 * point1.x * t * (1-t) * (1-t)
                        + 3 * point2.x * t * t * (1 - t)
                        + point3.x * t * t * t;
    
            pointF.y = point0.y * (1-t) * (1-t) * (1-t)
                        + 3 * point1.y * t * (1-t) * (1-t)
                        + 3 * point2.y * t * t * (1 - t)
                        + point3.y * t * t * t;
            return pointF;
        }
    }

5.最终总结

  5.1.自定义的RelativeLayout

/**
 * Created by ych on 2019/8/17.
 * Description: 花束直播点赞
 *      分析:1.点击的时候会有一张图片有放大和透明度的变化,图片向上延S路运动,图片是随机的图片
 *            2.移动的过程也有透明度变化
 *      思路:添加图片addView
 */
    public class LoveLayout extends RelativeLayout {
        //随机数
        private Random mRandom;
        //设置资源
        private int[] mImageRes;
        //屏幕宽高
        private int mWidth,mHeight;
        //图片的宽高
        private int mResWidth,mResheight;
        //插值器集合
        private Interpolator[] mInterpolator;
    
        public LoveLayout(Context context) {
            this(context,null);
        }
    
        public LoveLayout(Context context, AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            //1.初始化随机数
            mRandom = new Random();
            //2.初始化资源图片
            mImageRes = new int[]{R.drawable.green,R.drawable.red,R.drawable.note};
            //3.获取图片的宽高
            Drawable drawable = getContext().getResources().getDrawable(R.drawable.green);
            mResheight = drawable.getIntrinsicHeight();
            mResWidth = drawable.getIntrinsicWidth();
            //4.插值器集合
    //        mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator()
    //                                            ,new AccelerateInterpolator()
    //                                            ,new DecelerateInterpolator()
    //                                            ,new LinearInterpolator()};
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
    
        public void addView() {
            //创建ImageView,设置图片资源
            final ImageView ivLove = new ImageView(getContext());
            //设置图片资源随机
            ivLove.setImageResource(mImageRes[mRandom.nextInt(mImageRes.length)]);
            //设置图片添加位置
            RelativeLayout.LayoutParams params =  new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.addRule(ALIGN_PARENT_BOTTOM);
            params.addRule(CENTER_HORIZONTAL);
            ivLove.setLayoutParams(params);
            //添加到父容器
            addView(ivLove);
    
            //添加属性动画  放大和透明度效果
            AnimatorSet set = getAnimator(ivLove);
            set.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    //将view移除
                    removeView(ivLove);
                }
            });
            set.start();
    
        }
    
        private AnimatorSet getAnimator(ImageView ivLove) {
            AnimatorSet allAnimatorSet = new AnimatorSet();
    
            //1.设置动画,透明度,缩放
            AnimatorSet innerAnimator = new AnimatorSet();
            ValueAnimator alphaAnimator = ObjectAnimator.ofFloat(ivLove,"alpha",0.5f,1f);
            ValueAnimator scaleXAnimator = ObjectAnimator.ofFloat(ivLove,"scaleX",0.5f,1f);
            ValueAnimator scaleYAnimator = ObjectAnimator.ofFloat(ivLove,"scaleY",0.5f,1f);
            innerAnimator.setDuration(350);
            //一起执行
            innerAnimator.playTogether(alphaAnimator,scaleXAnimator,scaleYAnimator);
    
            //2.设置运动路径动画    playSequentially按顺序一次执行
            allAnimatorSet.playSequentially(innerAnimator,getBezierAnimator(ivLove));
    
            return allAnimatorSet;
        }
    
        /**
         * 贝塞尔路径动画  自定义TypeEvaluator
         * @return
         */
        private Animator getBezierAnimator(final ImageView ivLove) {
            //1.四个点 p0,p1,p2,p3
            PointF point0 = new PointF(mWidth / 2 - mResWidth / 2,mHeight - mResheight) ;
            //2.P2点的y坐标要大于p1点的y坐标
            PointF point1 = getPoint(1);
            PointF point2 = getPoint(2);
            //终点为屏幕的一半
            Log.e("getBezierAnimator",mHeight+"");
            PointF point3 = new PointF(mRandom.nextInt(mWidth - mResWidth),0);
    
            //构造方法中传入中间的点
            LoveTypeEvaluator typeEvaluator = new LoveTypeEvaluator(point1,point2);
            ValueAnimator bezierAnimator = ObjectAnimator.ofObject(typeEvaluator,point0,point3);
    //        bezierAnimator.setInterpolator(mInterpolator[mRandom.nextInt(mInterpolator.length -1)]);
            bezierAnimator.setDuration(6000);
            bezierAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //获取值
                    PointF pointF = (PointF) animation.getAnimatedValue();
                    //设置最终之
                    ivLove.setX(pointF.x);
                    ivLove.setY(pointF.y);
                    //逐渐消失
                    float alpha = animation.getAnimatedFraction(); //他是0-1的数
                    ivLove.setAlpha(1- alpha + 0.2f);
                }
            });
    
            return bezierAnimator;
        }
    
        /**
         * 1代表p1  2代表p2
         * p1的时候y最大值位 mHeight / 2
         * p2的时候y最大值 mHeight / 2 + ((2-1) *mHeight / 2) = mHeight
         * @param index
         * @return
         */
        private PointF getPoint(int index) {
            return new PointF(mRandom.nextInt(mWidth - mResWidth),mRandom.nextInt(mHeight / 2 +(index-1)*mHeight/2));
        }
    }

  5.2.自定义的TypeEvaluator

    /**
     * Created by ych on 2019/8/17.
     * Description:
     */
    public class LoveTypeEvaluator implements TypeEvaluator<PointF> {
        //中间点
        private PointF point1;
        private PointF point2;
    
        public LoveTypeEvaluator(PointF point1, PointF point2) {
            this.point1 = point1;
            this.point2 = point2;
        }
    
        /**
         *
         * @param t [0 ,1]
         * @param point0    起始点
         * @param point3    终点
         * @return
         */
        @Override
        public PointF evaluate(float t, PointF point0, PointF point3) {
            PointF pointF = new PointF();
            //直接套公式,三阶贝塞尔曲线
            pointF.x = point0.x * (1-t) * (1-t) * (1-t)
                        + 3 * point1.x * t * (1-t) * (1-t)
                        + 3 * point2.x * t * t * (1 - t)
                        + point3.x * t * t * t;
    
            pointF.y = point0.y * (1-t) * (1-t) * (1-t)
                        + 3 * point1.y * t * (1-t) * (1-t)
                        + 3 * point2.y * t * t * (1 - t)
                        + point3.y * t * t * t;
            return pointF;
        }
    }
上一篇下一篇

猜你喜欢

热点阅读