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

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

程序员文章站 2022-05-30 20:34:35
...

实现效果图

自定义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()方法对图片旋转,看下效果图
自定义View—利用Camera实现3D翻转动画

这时我们的旋转轴的坐标就是图片的上边沿,图片是围绕着上边沿旋转的。而仔细去看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();

    }

再看看效果,

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

就是这个效果,可以这么理解。图片是围绕着下边沿进行旋转的,图片的初始角度是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();

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

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

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的初始化,对初始旋转角度的初始化。实现效果后再对代码进行简化,整理,封装等等。

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