Android——自定义View(三)

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;
	    }
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值