Android自定义View——自定义插值器和估值器实现向上抛体运动

效果图

在这里插入图片描述

原理分析

其实代码很简单,主要的知识点就是执行动画是如何配合插值器和估值器的运算,达到我们想要的效果。这里的向上抛体运动和自由落体运动主要是运用了高中的加速度运算的公式,计算其x轴和y轴的路程和速度即可,通过插值器计算时间和估值器计算位移,达到向上抛体效果。

  • 插值器:用0-t,来表示时间的递增
  • 估值器:用公式计算出动画的路程
  • 当前速度:v = v0 - gt
  • 向上抛体运动:s = v0t + 1/2 gt^2
  • 自由落体运动:s = 1/2 gt^2

实现步骤

1、初始化变量

//1、继承RelativeLayout
public class GravityView extends RelativeLayout implements View.OnClickListener {

    private Context context;
    //2、准备几张图片
    private int[] christmas_drawable = {R.drawable.christmas01, R.drawable.christmas02, R.drawable.christmas03
            , R.drawable.christmas04, R.drawable.christmas05, R.drawable.christmas06};
    //随机数种子
    private Random random = new Random();
    //View的宽高
    private int width, height;
    //图片的宽高
    private int drawableWidth, drawableHeight;
    //动画播放时长
    private int time = 5;
    
    public GravityView(Context context) {
        this(context, null);
    }

    public GravityView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GravityView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        this.context = context;
        //3、设置点击事件
        setOnClickListener(this);
        //4、获取图片的宽高
        Drawable drawable = ContextCompat.getDrawable(context, R.drawable.christmas01);
        drawableWidth = drawable.getIntrinsicWidth();
        drawableHeight = drawable.getIntrinsicHeight();
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //获取View的宽高
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
    }
}

2、点击事件

@Override
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
public void onClick(View v) {
    //5、点击增加图片
    addChristmas(context);
}

/**
 * 添加图片
 *
 * @param context
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
private void addChristmas(Context context) {
    /**
     * 1、点击一次增加一张图片在底部
     */
    final ImageView imageView = new ImageView(context);
    imageView.setBackgroundResource(christmas_drawable[random.nextInt(christmas_drawable.length - 1)]);
    LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
    params.addRule(ALIGN_PARENT_BOTTOM);
    params.addRule(CENTER_HORIZONTAL);
    imageView.setLayoutParams(params);
    addView(imageView);

    //2、开始动画
    AnimatorSet animatorSet = getAnimatorSet(imageView);
    animatorSet.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            //3、动画执行后移除View
            removeView(imageView);
        }
    });
    animatorSet.start();
}

3、添加动画

/**
 * 添加动画
 *
 * @param imageView
 * @return
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
private AnimatorSet getAnimatorSet(ImageView imageView) {
    AnimatorSet enter = new AnimatorSet();

    //1、缩放动画
    AnimatorSet scaleAnimator = new AnimatorSet();
    ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 0.3f, 1f);
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(imageView, "scaleX", 0.3f, 1f);
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(imageView, "scaleY", 0.3f, 1f);
    scaleAnimator.setDuration(300);
    scaleAnimator.playTogether(alpha, scaleX, scaleY);

    //2、向上抛体动画
    ValueAnimator bezierAnimator = getGravityAnimator(imageView);

    //3、两个动画按顺序播放
    enter.playSequentially(scaleAnimator, bezierAnimator);
    return enter;
}

/**
 * 向上抛体动画
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
private ValueAnimator getGravityAnimator(final ImageView imageView) {
    //1、构建p0和p3点,p0就是底部中点,p3是不会用到的点,随便取个(0,0)
    PointF point0 = new PointF((width - drawableWidth) / 2, height - drawableHeight);
    PointF point3 = new PointF(0, 0);

    //2、创建估值器
    BezierEvaluator evaluator = new BezierEvaluator();
    final ValueAnimator valueAnimator = ObjectAnimator.ofObject(evaluator, point0, point3);
    //3、自定义插值器
    valueAnimator.setInterpolator(new XYInterpolator());
    valueAnimator.setDuration(time * 1000);
    //4、监听贝塞尔曲线估值器返回值
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            //5、获取BezierEvaluator中evaluate()返回的运行轨迹坐标点,设置图片路线
            PointF pointF = (PointF) animation.getAnimatedValue();
            imageView.setX(pointF.x);
            imageView.setY(pointF.y);
        }
    });
    return valueAnimator;
}

/**
 * 估值器:计算动画的执行轨迹
 */
public class BezierEvaluator implements TypeEvaluator<PointF> {

    double g = 1500; //物理中的g
    double v0 = 2000; //初速度

    double t1 = 0; //记录向上抛体顶点位置的时间
    double y1 = 0; //记录向上抛体顶点位置的y

    @Override
    public PointF evaluate(float t, PointF point0, PointF point3) {
        PointF point = new PointF();

        //保持x轴坐标不动
        point.x = point0.x;

        double v = v0 - (g * t);

        if (v <= 0) {
            //当物体到达顶点,速度开始小于0,开始自由落体运动,s = 1/2 gt^2
            point.y = (float) (y1 + (g * (t - t1) * (t - t1)) / 2);
        } else {
            //向上抛体运动,s = v0t + 1/2 gt^2
            point.y = (float) (point0.y - (v0 * t) + (g * t * t) / 2);

            t1 = t;
            y1 = point.y;
        }
        return point;
    }
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
public class XYInterpolator extends BaseInterpolator {
    @Override
    public float getInterpolation(float input) {
        // input是从0-1递增,所以会从0-time递增,也就是对应估值器的t的变化
        return time * input;
    }
}

其布局的实现

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.test.customView.GravityView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true" />

</RelativeLayout>
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许英俊潇洒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值