安卓属性动画之 - TypeEvaluator自定义计算器和TimeInterpolator实现自定义插值器

引言:
属性动画是一种很强大的东西,不同于补间动画,它在完成之后,会保留最终的状态。比如说,实现一个平移操作,最后View就会真的失去初始的位置和占用的空间,而获得一个新的位置和空间。

说明:
这里我们说明一下为什么要自定义上面两个东西。
1.对于Evaluator,可用的类型有好几种,比如Integer,Float等,他们是基于一个类型进行变换。如果我们想要实现从一个对象变到另一个对象,当然必须是是同一个类的对象。这种状态的过渡就需要我们自己重写evaluate方法来实现。
2.那又为什么要自定义插值器呢,因为有时候我们可能需要根据需求,在规定时间内对每个时间段动画变化快慢和方式进行调整,因此需要重写getInterpolation方法。可以把参数input理解为定义域为【0,1】的自变量x,我们要返回一个同样值域为【0,1】的因变量y,且y是x的函数。哈哈哈,太形象了吧。

实现TypeEvaluator:
说一下fraction,这是一个分数,在0到1之间,可以理解为进度。

    /**
     * 自定义计算器,计算[0,1]之间对象的每个对应状态
     * 我们这里改变点的坐标
     */
    public class PointEvaluator implements TypeEvaluator<Point> {
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            Point point=new Point();
            point.x= (int) (startValue.x+fraction*(endValue.x-startValue.x));
            point.y=(int)(startValue.y+fraction*(endValue.y-startValue.y));
            return point;
        }
    }

实现TimeInterpolator:
这个实现,是用来控制上面那个进度变化的速度,变化快慢。

    /**
     * 自定义插值器,即控制动画变换快慢,这里我们实现以正弦函数在【0,1】变换
     * 即斜率逐渐减小,动画逐渐变的迟缓
     */
    public class MyInterpolator implements TimeInterpolator{

        @Override
        public float getInterpolation(float input) {
            //正弦函数sin(Π*x/2),在定义域区间【0,1】取值为【0,1】,若定义的函数值域超出【0,1】
            //会出现回弹现象,即不能准确定位到我们定义属性动画时的最终状态
            return (float) Math.sin(input*Math.PI/2);
        }
    }

完整Demo:
给出完整的代码,实现一个“点击按钮就在屏幕上以正弦函数变化快慢绘制一个小圆的移动过程”的功能。

MainActivity:

package com.example.advancedanimatorusage;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.example.advancedanimatorusage.views.MyView;

public class MainActivity extends AppCompatActivity {

    private Button mStartAnimation;
    private MyView myView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        setClickEvent();
    }

    private void setClickEvent() {
        mStartAnimation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //开始动画时,隐藏按钮,显示MyView
                mStartAnimation.setVisibility(View.GONE);
                myView.setVisibility(View.VISIBLE);
            }
        });
    }

    private void initView() {
        mStartAnimation=findViewById(R.id.bt_start_animation);
        myView=findViewById(R.id.my_view_main);
    }

    public void setmStartAnimationVisibility() {
        //动画结束时,显示按钮,隐藏MyView
        mStartAnimation.setVisibility(View.VISIBLE);
        myView.setVisibility(View.GONE);
    }
}

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/bt_start_animation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Animation"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <com.example.advancedanimatorusage.views.MyView
        android:id="@+id/my_view_main"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

MyView(实现绘制的自定义View):

package com.example.advancedanimatorusage.views;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import com.example.advancedanimatorusage.MainActivity;

public class MyView extends View {

    private Paint mPaint;
    private static final int RADIUS=80;
    private Point currentPoint;
    private Point startPoint;  //初始状态
    private Point endPoint;   //最后状态
    private MainActivity mContext;

    public MyView(Context context) {
        super(context);
        init(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(currentPoint==null){
            currentPoint=new Point(RADIUS,RADIUS);
            canvas.drawCircle(currentPoint.x,currentPoint.y,RADIUS,mPaint);
            startAnimator();
        }else{
            canvas.drawCircle(currentPoint.x,currentPoint.y,RADIUS,mPaint);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        startPoint=new Point(RADIUS,RADIUS);
        endPoint=new Point(getMeasuredWidth()-RADIUS,getMeasuredHeight()-RADIUS);
    }

    private void init(Context context){
        //初始化画笔对象
        mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.RED);
        mContext= (MainActivity) context;
    }

    public void startAnimator(){
        //定义一个属性动画
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Point point= (Point) animation.getAnimatedValue();
                currentPoint.set(point.x,point.y);
                //调用该方法,会使View重新绘制,会清空画布,并且会重新调用onDraw方法
                invalidate();
            }
        });
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mContext.setmStartAnimationVisibility();
                currentPoint=null;
            }
        });
        valueAnimator.setInterpolator(new MyInterpolator());
        valueAnimator.setDuration(4000);
        valueAnimator.start();
    }


    /**
     * 自定义计算器,计算[0,1]之间对象的每个对应状态
     * 我们这里改变点的坐标
     */
    public class PointEvaluator implements TypeEvaluator<Point> {
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            Point point=new Point();
            point.x= (int) (startValue.x+fraction*(endValue.x-startValue.x));
            point.y=(int)(startValue.y+fraction*(endValue.y-startValue.y));
            return point;
        }
    }

    /**
     * 自定义插值器,即控制动画变换快慢,这里我们实现以正弦函数在【0,1】变换
     * 即斜率逐渐减小,动画逐渐变的迟缓
     */
    public class MyInterpolator implements TimeInterpolator{

        @Override
        public float getInterpolation(float input) {
            //正弦函数sin(Π*x/2),在定义域区间【0,1】取值为【0,1】,若定义的函数值域超出【0,1】
            //会出现回弹现象,即不能准确定位到我们定义属性动画时的最终状态
            return (float) Math.sin(input*Math.PI/2);
        }
    }
}

效果图由于是动态的,还不会制作gif~~~自己复制粘贴我的代码去试试吧,注意改一改引入的文件就好了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我很乐意为您介绍JavaScript实现房贷计算器的方法。 首先,我们需要了解房贷计算器的计算公式。一般来说,房贷计算器需要计算出每月还款金额、还款总额、支付利息总额等指标。这些指标的计算公式如下: 每月还款金额 = [贷款本金 &times; 月利率 &times; (1 + 月利率)^还款月数] ÷ [(1 + 月利率)^还款月数 - 1] 还款总额 = 每月还款金额 &times; 还款月数 支付利息总额 = 还款总额 - 贷款本金 了解了计算公式之后,我们就可以开始编写JavaScript代码了。以下是一个简单的房贷计算器实现代码示例: ```javascript // 获取页面元素 var loanAmount = document.getElementById("loan-amount"); var loanTerm = document.getElementById("loan-term"); var interestRate = document.getElementById("interest-rate"); var calculateButton = document.getElementById("calculate-button"); var resultMonthlyPayment = document.getElementById("result-monthly-payment"); var resultTotalPayment = document.getElementById("result-total-payment"); var resultTotalInterest = document.getElementById("result-total-interest"); // 计算每月还款金额 function calculateMonthlyPayment(loanAmount, loanTerm, interestRate) { var monthlyInterestRate = interestRate / 1200; // 月利率 var totalPayment = loanAmount * (1 + monthlyInterestRate * loanTerm); // 还款总额 var monthlyPayment = totalPayment / loanTerm; // 每月还款金额 return monthlyPayment.toFixed(2); // 保留两位小数 } // 计算房贷支付指标 function calculateLoanPayments() { var loanAmountValue = parseFloat(loanAmount.value); var loanTermValue = parseFloat(loanTerm.value); var interestRateValue = parseFloat(interestRate.value); var monthlyPayment = calculateMonthlyPayment(loanAmountValue, loanTermValue, interestRateValue); var totalPayment = monthlyPayment * loanTermValue; var totalInterest = totalPayment - loanAmountValue; resultMonthlyPayment.innerHTML = monthlyPayment.toFixed(2); resultTotalPayment.innerHTML = totalPayment.toFixed(2); resultTotalInterest.innerHTML = totalInterest.toFixed(2); } // 注册计算按钮点击事件 calculateButton.addEventListener("click", calculateLoanPayments); ``` 在上面的代码示例,我们首先通过`document.getElementById()`方法获取了页面的各个元素,然后定义了一个计算每月还款金额的函数`calculateMonthlyPayment()`,最后定义了一个计算房贷支付指标的函数`calculateLoanPayments()`。在`calculateLoanPayments()`函数,我们获取了用户输入的贷款金额、贷款期限和利率,然后调用`calculateMonthlyPayment()`函数计算每月还款金额,并根据公式计算出还款总额和支付利息总额。最后,我们将计算结果显示到页面相应的元素。 最后,我们还为计算按钮注册了一个点击事件,当用户点击按钮时,会自动计算房贷支付指标并显示到页面上。 以上就是一个简单的房贷计算器JavaScript实现方法。希望可以帮助到您!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tran_sient

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

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

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

打赏作者

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

抵扣说明:

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

余额充值