android 翻牌效果

翻牌效果网上很多,一搜一大把,有各种实现方法,我就记录下我觉得简单效果好的方法,一个是属性动画ObjectAnimator实现,还有一个是android.graphics.Camera(不是拍照录像的Camera),我比较在意的是用Camera这个类实现的3d效果
网上的Camera例子基本都是用Rotate3dAnimation.java,我就随便拿了个进行测试,发现有bug,后来找到这篇文章安卓自定义View进阶-Matrix Camera,原来Rotate3dAnimation.java是一个演示demo,而这篇文章也把这个bug解决了,而且这篇文章里的推荐控件也很强大
我在Canvas内进行了简单的测试,看看Camera效果

Rotate3dView.java,翻转代码用的就是Rotate3dAnimation.java内的代码

public class Rotate3dView extends View {
    private Bitmap frontBitmap;
    private Bitmap backBitmap;
    private Paint paint;
    private Matrix matrix;
    private Camera camera;
    private  float[] mValues = new float[9];
    private float scale = 1;
    public Rotate3dView(Context context) {
        super(context);
        init();
    }

    public Rotate3dView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private void init(){
        camera = new Camera();
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 8;
        frontBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_jn,options);
        backBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_m,options);
        paint = new Paint();
        paint.setColor(Color.RED);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        matrix = new Matrix();
        scale = getResources().getDisplayMetrics().density;
    }
    private int degrees = 0;

    public void setDegrees(int degrees) {
        this.degrees = degrees;
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(getWidth() == 0 || getHeight() == 0){
            return;
        }
        float centerX = getWidth()/2;
        float centerY = getHeight()/2;
        float left = centerX - frontBitmap.getWidth()/2;
        float top = centerY - frontBitmap.getHeight()/2;
        canvas.save();
        camera.save();
        matrix.setTranslate(0,0);
        if(degrees < 90){
            camera.rotateY(degrees);
        }else{
            camera.rotateY(degrees - 180);
        }

        camera.getMatrix(matrix);

        camera.restore();


        matrix.getValues(mValues);           //获取数值
        mValues[6] = mValues[6]/scale;       //数值修正
        mValues[7] = mValues[7]/scale;       //数值修正
        matrix.setValues(mValues);           //重新赋值
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);

//        matrix.postTranslate(100, 100);
        canvas.setMatrix(matrix);
        if(degrees < 90){
            canvas.drawBitmap(frontBitmap,left ,top,null);
        }else{
            canvas.drawBitmap(backBitmap,left ,top,null);
        }

        canvas.restore();

        canvas.drawRect(left,top,left+frontBitmap.getWidth(),top+frontBitmap.getHeight(),paint);

        canvas.drawLine(centerX,0,centerX,getHeight(),paint);
        canvas.drawLine(0,centerY,getWidth(),centerY,paint);


    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hyq.hm.testdraw.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#888">
        <SeekBar
            android:id="@+id/seek_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:max="180"/>
        <com.hyq.hm.testdraw.Rotate3dView
            android:id="@+id/rotate_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>


</android.support.constraint.ConstraintLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private SeekBar seekBar;
    private Rotate3dView rotate3dView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rotate3dView = findViewById(R.id.rotate_view);

        seekBar = findViewById(R.id.seek_bar);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                rotate3dView.setDegrees(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

    }

}

接下来贴翻牌动画代码,用属性动画ObjectAnimator实现的代码都注释掉了,可以把注释去掉,再把Rotate3dAnimation代码注释掉看看ObjectAnimator的效果,我觉得没有Rotate3dAnimation的好

Rotate3dAnimation.java

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;
    private float scale = 1;
    private int mXY;
    /**
     * 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。
     *
     * @param fromDegrees  起始时角度
     * @param toDegrees    结束时角度
     * @param centerX     旋转中心x坐标
     * @param centerY     旋转中心y坐标
     * @param depthZ      最远到达的z轴坐标
     * @param reverse     true 表示由从0到depthZ,false相反
     */
    public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,
                             float centerX, float centerY, float depthZ, boolean reverse,int xy) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mCenterX = centerX;
        mCenterY = centerY;
        mDepthZ = depthZ;
        mReverse = reverse;
        scale = context.getResources().getDisplayMetrics().density;
        mXY = xy;

    }
    @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();
        camera.save();

        // 调节深度
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }

        if(mXY == 1){
            camera.rotateX(degrees);
        }else{
            camera.rotateY(degrees);
        }


        camera.getMatrix(matrix);
        camera.restore();

        // 修正失真,主要修改 MPERSP_0 和 MPERSP_1
        float[] mValues = new float[9];
        matrix.getValues(mValues);           //获取数值
        mValues[6] = mValues[6]/scale;       //数值修正
        mValues[7] = mValues[7]/scale;       //数值修正
        matrix.setValues(mValues);           //重新赋值


        // 调节中心点
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);

    }
}

activity_rotate_3d.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="演示"
            android:onClick="startAnimation"/>
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ImageView
                android:id="@+id/front_view"
                android:layout_width="300dp"
                android:layout_height="300dp"
                android:layout_centerInParent="true" />
            <ImageView
                android:id="@+id/back_view"
                android:layout_width="300dp"
                android:layout_height="300dp"
                android:layout_centerInParent="true"
                android:visibility="invisible"
                />
        </RelativeLayout>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

Rotate3dActivity.java

public class Rotate3dActivity extends AppCompatActivity {
    private ImageView[] imageViews = new ImageView[2];
    private int index = 0;
//    private ObjectAnimator frontAnim;
//    private ObjectAnimator backAnim;
    private Animation frontAnim;
    private Animation backAnim;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rotate_3d);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 4;
        imageViews[0] = findViewById(R.id.front_view);
        imageViews[0].setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.ic_jn,options));
        imageViews[1] = findViewById(R.id.back_view);
        imageViews[1].setImageBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.ic_m,options));

//        frontAnim = new ObjectAnimator();
//        frontAnim.setFloatValues(0,90f);
//        frontAnim.setDuration(1000);
//        frontAnim.setInterpolator(new LinearInterpolator());
//        frontAnim.addListener(new Animator.AnimatorListener() {
//            @Override
//            public void onAnimationStart(Animator animation) {
//
//            }
//
//            @Override
//            public void onAnimationEnd(Animator animation) {
//                imageViews[index].setVisibility(View.INVISIBLE);
//                index = (index+1)%2;
//                imageViews[index].setVisibility(View.VISIBLE);
//                backAnim.start();
//            }
//
//            @Override
//            public void onAnimationCancel(Animator animation) {
//
//            }
//
//            @Override
//            public void onAnimationRepeat(Animator animation) {
//
//            }
//        });
//        ValueAnimator.AnimatorUpdateListener updateListener = new ValueAnimator.AnimatorUpdateListener() {
//            @Override
//            public void onAnimationUpdate(ValueAnimator animation) {
//                float value = (Float) animation.getAnimatedValue();
//                imageViews[index].setRotationY(value);
//            }
//        };
//        frontAnim.addUpdateListener(updateListener);
//
//        backAnim = new ObjectAnimator();
//        backAnim.setFloatValues(270f,360f);
//        backAnim.setDuration(1000);
//        backAnim.setInterpolator(new LinearInterpolator());
//        backAnim.addListener(new Animator.AnimatorListener() {
//            @Override
//            public void onAnimationStart(Animator animation) {
//            }
//
//            @Override
//            public void onAnimationEnd(Animator animation) {
//                isAnim = false;
//            }
//
//            @Override
//            public void onAnimationCancel(Animator animation) {
//            }
//
//            @Override
//            public void onAnimationRepeat(Animator animation) {
//            }
//        });
//        backAnim.addUpdateListener(updateListener);

    }
    private boolean isAnim = false;
    public void startAnimation(View view){
        if(isAnim){
            return;
        }
        isAnim = true;
        if(frontAnim == null){
            final float centerX = imageViews[0].getWidth() / 2.0f;
            final float centerY = imageViews[0].getHeight() / 2.0f;
            frontAnim= new Rotate3dAnimation(this,0, 90, centerX, centerY, 0f, true,0);
            frontAnim.setDuration(1000);                         //设置动画时长
            frontAnim.setFillAfter(true);                        //保持旋转后效果
            frontAnim.setInterpolator(new LinearInterpolator());   //设置插值器

            backAnim = new Rotate3dAnimation(this,-90, 0, centerX, centerY, 0f, true,0);
            backAnim.setDuration(1000);
            backAnim.setFillAfter(true);
            backAnim.setInterpolator(new LinearInterpolator());

            frontAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    imageViews[index].setVisibility(View.INVISIBLE);
                    imageViews[index].clearAnimation();
                    index = (index+1)%2;
                    imageViews[index].setVisibility(View.VISIBLE);
                    imageViews[index].startAnimation(backAnim);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            backAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    isAnim = false;
                    imageViews[index].clearAnimation();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
        }
        imageViews[index].startAnimation(frontAnim);


//        frontAnim.start();
    }
}

还是要好好研究研究android.graphics.Camera

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值