美文网首页Android UIandnroidAndroid开发知识集
Android 利用Camera实现中轴3D卡牌翻转效果

Android 利用Camera实现中轴3D卡牌翻转效果

作者: 亦枫 | 来源:发表于2015-12-28 23:56 被阅读8942次

    在Android系统API中,有两个Camera类:

    • android.graphics.Camera
    • android.hardware.Camera

    第二个应用于手机硬件的中相机相关的操作,本文讲述的是利用第一个Camera类实现中轴3D转换的卡牌翻转效果,如图:

    3d立体旋转效果.gif

    开始之前,先看一下Android系统中的坐标系:

    坐标系.png

    对应于三维坐标系中的三个方向,Camera提供了三种旋转方法:

    • rotateX()
    • rotateY()
    • rotateX()

    调用这三种方法,传入旋转角度参数,即可实现视图沿着坐标轴旋转的功能。本文的中轴3D旋转效果就是让视图沿着Y轴旋转的。

    系统API Demos中已经为我们提供了一个非常好用的3D旋转动画的工具类:
    Rotate3dAnimation.java:

    package com.feng.androidtest;
    
    import android.graphics.Camera;
    import android.graphics.Matrix;
    import android.util.Log;
    import android.view.animation.Animation;
    import android.view.animation.Transformation;
    
    /**
     * An animation that rotates the view on the Y axis between two specified angles.
     * This animation also adds a translation on the Z axis (depth) to improve the effect.
     */
    public class Rotate3dAnimation extends Animation {
        private final float mFromDegrees;
        private final float mToDegrees;
        private final float mCenterX;
        private final float mCenterY;
        private final float mDepthZ;
        private final boolean mReverse;
        private Camera mCamera;
    
        /**
         * Creates a new 3D rotation on the Y axis. The rotation is defined by its
         * start angle and its end angle. Both angles are in degrees. The rotation
         * is performed around a center point on the 2D space, definied by a pair
         * of X and Y coordinates, called centerX and centerY. When the animation
         * starts, a translation on the Z axis (depth) is performed. The length
         * of the translation can be specified, as well as whether the translation
         * should be reversed in time.
         *
         * @param fromDegrees the start angle of the 3D rotation
         * @param toDegrees the end angle of the 3D rotation
         * @param centerX the X center of the 3D rotation
         * @param centerY the Y center of the 3D rotation
         * @param reverse true if the translation should be reversed, false otherwise
         */
        public Rotate3dAnimation(float fromDegrees, float toDegrees,
                float centerX, float centerY, float depthZ, boolean reverse) {
            mFromDegrees = fromDegrees;
            mToDegrees = toDegrees;
            mCenterX = centerX;
            mCenterY = centerY;
            mDepthZ = depthZ;
            mReverse = reverse;
        }
    
        @Override
        public void initialize(int width, int height, int parentWidth, int parentHeight) {
            super.initialize(width, height, parentWidth, parentHeight);
            mCamera = new Camera();
        }
    
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            final float fromDegrees = mFromDegrees;
            float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
    
            final float centerX = mCenterX;
            final float centerY = mCenterY;
            final Camera camera = mCamera;
    
            final Matrix matrix = t.getMatrix();
    
            Log.i("interpolatedTime", interpolatedTime+"");
            camera.save();
            if (mReverse) {
                camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
            } else {
                camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
            }
            camera.rotateY(degrees);
            camera.getMatrix(matrix);
            camera.restore();
    
            matrix.preTranslate(-centerX, -centerY);
            matrix.postTranslate(centerX, centerY);
        }
    }
    

    可以看出, Rotate3dAnimation 总共做了两件事:在构造函数中赋值了旋转动画所需要的参数,以及重写(override)父类Animation中的applyTransformation()方法,下面分类阐述一下:

    • fromDegrees与toDegrees
      视图旋转的开始角度和结束角度,当toDegree处于90倍数时,视图将变得不可见。

    • centerX与centerY
      视图旋转的中心点。

    • depthZ
      Z轴移动基数,用于计算Camera在Z轴移动距离

    • reverse
      boolean类型,控制Z轴移动方向,达到视觉远近移动导致的视图放大缩小效果。

    • applyTransformation()
      根据动画播放的时间 interpolatedTime (动画start到end的过程,interpolatedTime从0.0变化到1.0),让Camera在Z轴方向上进行相应距离的移动,实现视觉上远近移动的效果。然后调用 rotateX()方法,让视图围绕Y轴进行旋转,产生3D立体旋转效果。最后再通过Matrix来确定旋转的中心点的位置。

    activity_main.xml布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@android:color/white" >
    
        <Button
            android:id="@+id/btn_open"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:onClick="onClickView"
            android:text="打开"
            android:textColor="@android:color/black"
            android:textSize="16sp" />
    
        <RelativeLayout
            android:id="@+id/rl_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/btn_open"
            android:layout_marginTop="16dp" 
            android:background="@android:color/black">
    
            <ImageView
                android:id="@+id/iv_logo"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:contentDescription="@null"
                android:src="@drawable/ic_qrcode" 
                android:scaleType="centerInside"/>
    
            <TextView
                android:id="@+id/tv_desc"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="16dp"
                android:text="技术鸟\n\n微信号:NiaoTech\n\n上谈【安卓】,下论【苹果】.以扯淡的态度,面对操蛋的技术,用幽默的语言,诠释开发的经典。"
                android:textColor="@android:color/white"
                android:textSize="18sp" 
                android:visibility="gone"/>
        </RelativeLayout>
    
    </RelativeLayout>
    

    布局中配置了卡牌正面的图片控件,卡牌背面的文本控件,以及他们的parent容器,也就是本文中的旋转动画的执行对象。

    MainActivity.java文件:

    package com.feng.androidtest;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.animation.AccelerateInterpolator;
    import android.view.animation.Animation;
    import android.view.animation.Animation.AnimationListener;
    import android.view.animation.DecelerateInterpolator;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    
    import com.example.androidtest.R;
    
    /**
     * 
     * @author 亦枫,微信公众号:技术鸟
     * @see http://blog.csdn.net/growing_tree
     */
    public class MainActivity extends Activity {
        
        private RelativeLayout mContentRl;
        private ImageView mLogoIv;
        private TextView mDescTv;
        private Button mOpenBtn;
        
        private int centerX;
        private int centerY;
        private int depthZ = 400;
        private int duration = 600;
        private Rotate3dAnimation openAnimation;
        private Rotate3dAnimation closeAnimation;
        
        private boolean isOpen = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            mContentRl = (RelativeLayout) findViewById(R.id.rl_content);
            mLogoIv = (ImageView) findViewById(R.id.iv_logo);
            mDescTv = (TextView) findViewById(R.id.tv_desc);
            mOpenBtn = (Button) findViewById(R.id.btn_open);
            
        }
        
        /**
         * 卡牌文本介绍打开效果:注意旋转角度
         */
        private void initOpenAnim() {
            //从0到90度,顺时针旋转视图,此时reverse参数为true,达到90度时动画结束时视图变得不可见,
            openAnimation = new Rotate3dAnimation(0, 90, centerX, centerY, depthZ, true);
            openAnimation.setDuration(duration);
            openAnimation.setFillAfter(true);
            openAnimation.setInterpolator(new AccelerateInterpolator());
            openAnimation.setAnimationListener(new AnimationListener() {
                
                @Override
                public void onAnimationStart(Animation animation) {
                    
                }
                
                @Override
                public void onAnimationRepeat(Animation animation) {
                    
                }
                
                @Override
                public void onAnimationEnd(Animation animation) {
                    mLogoIv.setVisibility(View.GONE);
                    mDescTv.setVisibility(View.VISIBLE);
                    
                    //从270到360度,顺时针旋转视图,此时reverse参数为false,达到360度动画结束时视图变得可见
                    Rotate3dAnimation rotateAnimation = new Rotate3dAnimation(270, 360, centerX, centerY, depthZ, false);
                    rotateAnimation.setDuration(duration);
                    rotateAnimation.setFillAfter(true);
                    rotateAnimation.setInterpolator(new DecelerateInterpolator());
                    mContentRl.startAnimation(rotateAnimation);
                }
            });
        }
        
        /**
         * 卡牌文本介绍关闭效果:旋转角度与打开时逆行即可
         */
        private void initCloseAnim() {
            closeAnimation = new Rotate3dAnimation(360, 270, centerX, centerY, depthZ, true);
            closeAnimation.setDuration(duration);
            closeAnimation.setFillAfter(true);
            closeAnimation.setInterpolator(new AccelerateInterpolator());
            closeAnimation.setAnimationListener(new AnimationListener() {
                
                @Override
                public void onAnimationStart(Animation animation) {
                    
                }
                
                @Override
                public void onAnimationRepeat(Animation animation) {
                    
                }
                
                @Override
                public void onAnimationEnd(Animation animation) {
                    mLogoIv.setVisibility(View.VISIBLE);
                    mDescTv.setVisibility(View.GONE);
                    
                    Rotate3dAnimation rotateAnimation = new Rotate3dAnimation(90, 0, centerX, centerY, depthZ, false);
                    rotateAnimation.setDuration(duration);
                    rotateAnimation.setFillAfter(true);
                    rotateAnimation.setInterpolator(new DecelerateInterpolator());
                    mContentRl.startAnimation(rotateAnimation);
                }
            });
        }
        
        public void onClickView(View v) {
            //以旋转对象的中心点为旋转中心点,这里主要不要再onCreate方法中获取,因为视图初始绘制时,获取的宽高为0
            centerX = mContentRl.getWidth()/2;
            centerY = mContentRl.getHeight()/2;
            if (openAnimation == null) {
                initOpenAnim();
                initCloseAnim();
            }
            
            //用作判断当前点击事件发生时动画是否正在执行
            if (openAnimation.hasStarted() && !openAnimation.hasEnded()) {
                return;
            }
            if (closeAnimation.hasStarted() && !closeAnimation.hasEnded()) {
                return;
            }
            
            //判断动画执行
            if (isOpen) {
                mContentRl.startAnimation(closeAnimation);
                
            }else {
                
                mContentRl.startAnimation(openAnimation);
            }
            
            isOpen = !isOpen;
            mOpenBtn.setText(isOpen ? "关闭" : "打开");
        }
    }
    
    

    代码中已对核心的地方做了注释解释,主要弄清楚 rotate3dAnimation构造参数中的 fromDegrees和toDegrees、depthZ、reverse参数,同时在动画中设置了速度插播器,如动画的前半程使用加速器 AccelerateInterpolator,后半程使用减速器 DecelerateInterpolator
    ,使动画体验更加人性化。

    Demo下载:
    http://download.csdn.net/detail/wenbitianxiafeng/9382453

    欢迎关注作者微信公众号【技术鸟】,分享技术,共同进步!

    微信公众号【技术鸟】_二维码.gif

    相关文章

      网友评论

      • WangRain1:这种反转用不到camera吧,view自带旋转缩放就搞定了吧
      • d859e0e4414a:大佬,请问知道怎么去除图片翻转过程中的锯齿吗
      • f3f5e77b3893:给力,谢谢大佬!
      • 夏侯撒:厉害。学习中
      • 8d82cfe2023e:当viewGroup中层次多或者资源多的时候会有卡顿 这个怎么解决呢
      • 修得养得梦得过得:不错
        亦枫:@修得养得梦得过得 谢谢
      • 键盘男:以前自己做的翻转动画好难看,原来是没有z轴偏移....如果不用Camera,用translationZ能实现吗?
        壞氣足:@Showdy 怎么做成上下翻转的啊
        Showdy:@键盘男kkmike999 那是2d旋转,没有z轴的偏移
      • 肉团先生:“系统API Demos中已经为我们提供了”。求教育这里怎么知道系统api demo提供了,系统api demo哪里获取呢?Thx
        亦枫:@Trity 欢迎交流,一起学习:smiley:
        肉团先生:@亦枫 找到了,原谅无知。当初想做这个效果,却不知道怎么搜索,没找到这个类。 :sweat:
        亦枫:@Trity sdk里面的demo呢
      • 曾樑::+1::+1:
        亦枫:@曾樑 给力:smile:

      本文标题:Android 利用Camera实现中轴3D卡牌翻转效果

      本文链接:https://www.haomeiwen.com/subject/guobhttx.html