自定义View—利用Camera实现3D翻转动画

实现效果图

这里写图片描述

做这个效果时,开始觉得很懵逼,无从下手,但冷静下来想一想,就是两张图片同时在执行旋转动画,那么我就从一张图片的旋转动画开始摸索。
好,现在就从画一张图开始

public class Roll3DView extends View {

    private Bitmap bitmap;
    private Paint paint;
    private Camera camera;
    private Matrix matrix;
    private int viewWidth;
    private int viewHeight;
    private ValueAnimator animator;
    private float degree;

    public Roll3DView(Context context) {
        this(context,null);
    }

    public Roll3DView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        bitmap = ((BitmapDrawable)(getResources().getDrawable(R.drawable.img1))).getBitmap();
        camera = new Camera();
        matrix = new Matrix();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = getMeasuredWidth();
        viewHeight = getMeasuredHeight();

        bitmap  = scaleBitmap(bitmap);

    }

    /**
     * 根据给定的宽和高进行拉伸
     *
     * @param origin 原图
     * @return new Bitmap
     */
    private Bitmap scaleBitmap(Bitmap origin) {
        if (origin == null) {
            return null;
        }
        int height = origin.getHeight();
        int width = origin.getWidth();
        float scaleWidth = ((float) viewWidth) / width;
        float scaleHeight = ((float) viewHeight) / height;
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
        Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
        return newBM;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save(); 
        camera.getMatrix(matrix);
        canvas.drawBitmap(bitmap,matrix,paint);
        canvas.restore();
    }

自定义Roll3DView,初始化Paint,Camera,Matrix。在onMeasure里对View进行测量,然后对图片进行了适当的伸缩处理。然后就是onDraw方法画出这张图。效果图先不看了,就是一张图。
下面呢,我想让这张做旋转动画,围绕x轴旋转90度,因此我写了startAnimation方法

public void startAnimation(){
        animator = ValueAnimator.ofFloat(0,90);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                degree = (float) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(2000);
        animator.start();
    }

当我点击图片的时候开始执行startAnimation,变量degree从0到90度变化,动画执行时间为2s,那么onDraw方法要修改

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        camera.save();
        camera.rotateX(degree);
        camera.getMatrix(matrix);
        camera.restore();
        canvas.drawBitmap(bitmap,matrix,paint);
        canvas.restore();

    }

调用Camera的rotateX()方法对图片旋转,看下效果图
这里写图片描述

这时我们的旋转轴的坐标就是图片的上边沿,图片是围绕着上边沿旋转的。而仔细去看3D效果的Vertical&toNext效果,第一张图的旋转时围绕着图的下边沿旋转的。那么我们就要先将轴线平移到图片的下边沿,然后再旋转的过程中再慢慢回移。修改onDraw

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        camera.save();
        camera.rotateX(degree);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-viewWidth/2,-viewHeight);
        matrix.postTranslate(viewWidth/2,(1-rate)*viewHeight);
        canvas.drawBitmap(bitmap,matrix,paint);
        canvas.restore();

    }

再看看效果,

这里写图片描述

就是这个效果,可以这么理解。图片是围绕着下边沿进行旋转的,图片的初始角度是0,先移动轴线到图片的下边沿,让图片围绕着下边沿旋转,然后在旋转的过程中将轴线从y=viewHeight位置慢慢移动到坐标y=0。

那么同理,接着看3D翻转Vertical&toNext效果,第二张图翻上来的动画,第二张图片是围绕上图片的上边沿进行旋转的,图片的初始角度是-90度,轴线不用改变,在旋转的过程中将轴线从y=viewHeight位置移动到y=0。

使用同一个animator, degree还是从0到90度变化,代码如下

canvas.save();
        camera.save();
        camera.rotateX(degree-90);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-viewWidth/2,0);
        matrix.postTranslate(viewWidth/2,(1-rate)*viewHeight);
        canvas.drawBitmap(bitmap,matrix,paint);
        canvas.restore();

看效果
这里写图片描述

两个单独的效果实现了,所以下面就将两个效果合起来,画两张图。

Bitmap curBitmap = bitmapList.get(curIndex);
        Bitmap nextBitmap = bitmapList.get(nextIndex);
        canvas.save();
        camera.save();
        camera.rotateX(rotatedDegree);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-viewWidth / 2, -viewHeight);
        matrix.postTranslate(viewWidth / 2, (1 - rate) * viewHeight);
        canvas.drawBitmap(curBitmap, matrix, paint);

        camera.save();
        camera.rotateX(rotatedDegree - 90);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-viewWidth / 2, 0);
        matrix.postTranslate(viewWidth / 2, (1 - rate) * viewHeight);
        canvas.drawBitmap(nextBitmap, matrix, paint);
        canvas.restore();

代码到这里当然还没结束,只是分析了其中的一种情况,而其他的情况都跟这中情况是类似的,无非就是是垂直翻转或水平翻转,下一张,前一张的区别,思想都是一样的,代码我也不贴了,如果是想实现这效果的话,最好是自己写个demo,一点点去实现,慢慢琢磨研究,才会记忆深刻。如果你认真写了,你就会发现其实很有很多细节没有处理呢,例如下面:

public void toNext() {
        if (isRolling) return;
        animator = ValueAnimator.ofFloat(0, 90);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                rotatedDegree = (float) animation.getAnimatedValue();
                setRotateDegree(rotatedDegree);
                invalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isRolling = false;
                setRotateDegree(0);
                invalidate();
                curIndex++;
                if (curIndex > bitmapList.size() - 1) {
                    curIndex = 0;
                }
                initIndex();
            }
        });
        animator.setDuration(1000);
        isNext = true;
        isRolling = true;
        animator.start();
    }
public void setRotateDegree(float rotateDegree) {
        this.rotatedDegree = rotateDegree;
        rate = rotateDegree / 90;
    }
private void initIndex() {

        nextIndex = curIndex + 1;
        preIndex = curIndex - 1;
        if (nextIndex > bitmapList.size() - 1) {
            nextIndex = 0;
        }
        if (preIndex < 0) {
            preIndex = bitmapList.size() - 1;
        }
    }

isRolling是判断当前动画是否执行完成,对图片下标index的初始化,对初始旋转角度的初始化。实现效果后再对代码进行简化,整理,封装等等。

欢迎大家提出不同的理解和看法,共同学习,进步。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值