欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 自定义View----属性动画

程序员文章站 2022-05-29 18:14:55
...

Android 自定义View----属性动画

自定义view难免会接触到动画,不然很多效果做不出来,动画分为几种,今天主要用到的是属性动画;

上面这个效果分上下两部分完成,在绘制的时候先绘制下半部分,具体代码如下,里面有详细注释:

public class CameraAnimatorView extends View {

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    // 图片左上padding值
    private int mPadding = 300;
    // 图片宽高
    private int mLength = 800;

    // 上面翻的度数 (因为要做动画,所以先全部设置为0,然后设置get,set方法为动画做准备)
    float topFlip = 0;
    // 下面翻的度数
    float bottomFlip = 0;
    // 旋转度数
    float flipRotation = 0;

    /*
    android.graphics.Camera ( 是这个并不是相机,相机是android.hardware.Camera )
    官方介绍:A camera instance can be used to compute 3D transformations and generate a matrix that can be applied, for instance, on aCanvas.
              (自己随便翻译吧)
    Camera对象可以用来计算3D变换,并将计算结果封装进一个Matrix矩阵,之后便进行应用
     */
    Camera camera = new Camera();

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

    {
        /*
        默认0,0,-8  ( 8 英寸 = 8 * 72 像素 )
        每个手机分辨率不同,所以需要做适配 ,getResources().getDisplayMetrics().density
         */
        camera.setLocation(0, 0, -6 * getResources().getDisplayMetrics().density);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 先绘制下半部分,再绘制上半部分(直接复制)
        /*********************************绘制上半部分*******************************************/
        canvas.save();
        canvas.translate(mPadding + mLength / 2, mPadding + mLength / 2);
        canvas.rotate(-flipRotation);
        camera.save();
        // 以X轴为轴心旋转*°
        camera.rotateX(topFlip);
        camera.applyToCanvas(canvas);
        camera.restore();
        canvas.clipRect(-mLength , -mLength , mLength, 0);
        canvas.rotate(flipRotation);
        canvas.translate(-(mPadding + mLength / 2), -(mPadding + mLength / 2));
        canvas.drawBitmap(getAvatar(mLength), mPadding, mPadding, mPaint);
        canvas.restore();

        /**********************************绘制下半部分(倒着绘制)******************************/
        canvas.save();
        /*
        投射原点再0,0位置,如果不移动画布,映射出来的是变形的图片(歪的),所以需要先移动再旋转再挪回来
        挪动位置:padding + 图片宽高的一半
         */
        canvas.translate(mPadding + mLength / 2, mPadding + mLength / 2);
        canvas.rotate(-flipRotation);

        // 保存状态
        camera.save();
        // 以X轴为轴心旋转*°
        camera.rotateX(bottomFlip);
        // 应用到画布  根据当前的变换计算出相应的矩阵,然后应用到制定的画布上
        camera.applyToCanvas(canvas);
        // 回滚状态
        camera.restore();

        // 只要下半部分,旋转前切割  因为涉及到旋转,所以切割范围要变大
        canvas.clipRect(-mLength, 0, mLength, mLength);

        // 旋转20°
        canvas.rotate(flipRotation);

        // 将画布移回来
        canvas.translate(-(mPadding + mLength / 2), -(mPadding + mLength / 2));

        // 绘制图片
        canvas.drawBitmap(
                getAvatar(mLength),
                mPadding, mPadding,
                mPaint
        );
        canvas.restore();

    }

    // 自定义动画配置 topFlip,bottomFlip,FlipRotation
    public float getTopFlip() {
        return topFlip;
    }

    public void setTopFlip(float topFlip) {
        this.topFlip = topFlip;
        invalidate();
    }

    public float getBottomFlip() {
        return bottomFlip;
    }

    public void setBottomFlip(float bottomFlip) {
        this.bottomFlip = bottomFlip;
        invalidate();
    }

    public float getFlipRotation() {
        return flipRotation;
    }

    public void setFlipRotation(float flipRotation) {
        this.flipRotation = flipRotation;
        invalidate();
    }

    Bitmap getAvatar(int width) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        // inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸
        options.inJustDecodeBounds = true;
        // 从资源中读取(比较浪费资源,所以上面设置为true,只获取图片宽高)
        BitmapFactory.decodeResource(getResources(), R.drawable.ysdry, options);
        // 再设置为false,最后要返回bitmap
        options.inJustDecodeBounds = false;
        // 根据缩放比例重新计算宽高
        options.inDensity = options.outWidth;
        options.inTargetDensity = width;
        return BitmapFactory.decodeResource(getResources(), R.drawable.ysdry, options);
    }

}

下面是开启动画

       /*
        属性动画 大概分为两种,ViewPropertyAnimator 和 ObjectAnimator
                 前者是系统提供好的,可做一些简单的平移,缩放,旋转,渐变
                 后者是自定义属性动画,比较灵活
         */
        CameraAnimatorView mCameraAnimatorView = findViewById(R.id.mCameraAnimatorView);

        // bottomFlip对应CameraAnimatorView里面的bottomFlip,45代表bottomFlip要变化的值
        ObjectAnimator bottomAnimator=ObjectAnimator.ofFloat(mCameraAnimatorView,"bottomFlip",45);
        // 设置时长
        bottomAnimator.setDuration(1500);

        ObjectAnimator flipRotationAnimator=ObjectAnimator.ofFloat(mCameraAnimatorView,"flipRotation",270);
        flipRotationAnimator.setDuration(1500);

        ObjectAnimator topFlipAnimator=ObjectAnimator.ofFloat(mCameraAnimatorView,"topFlip",-45);
        flipRotationAnimator.setDuration(3300);

        // 多个动画,分先后顺序填写到playSequentially方法中
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(bottomAnimator,flipRotationAnimator,topFlipAnimator);
        // 延迟动画启动的时间
        animatorSet.setStartDelay(2000);
        animatorSet.start();