android - 3D旋转

参考:
camera、Matrix概念
Matrix的set,pre,post调用顺序

常用的API如下:
rotateX(float degree) 绕着x轴旋转degree个度数
rotateY(float degree) 绕着y轴旋转degree个度数
rotateZ(float degree) 绕着z轴旋转degree个度数
translate(float x,float y,float z) 平移一段距离
save()和restore() 作用跟Canvas的一样,保存原状态,操作完之后,恢复到原状态。

效果图:

这里写图片描述

好吧,电脑没显卡的弊端。。。

Rotate3dAnimation:3D动画

package com.example.rotatepicbrowserdemo;

import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.graphics.Camera;
import android.graphics.Matrix;

/**
 * 在两个指定角度之间绕Y轴旋转视图的动画。(3D动画)
 * <p>
 * 这个动画也在z轴(深度)上添加了一个转化来提高效果。
 */
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;

    /**
     * 在Y轴上创建一个新的3D旋转。旋转是由它的开始角和它的结束角来定义的。
     * <p>
     * 旋转围绕在二维空间的一个中心点,确定由一对X和Y坐标,称为centerX 和 centerY.
     * <p>
     * 当动画开始时,执行z轴(深度)上的转化。可以指定转化的长度,以及转化是否应该及时倒转。
     *
     * @param fromDegrees the start angle of the 3D rotation:3D旋转 起始角度
     * @param toDegrees   the end angle of the 3D rotation:3D旋转 结束角度
     * @param centerX     the X center of the 3D rotation:3D旋转 x轴中心
     * @param centerY     the Y center of the 3D rotation:3D旋转 Y轴中心
     * @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();

        camera.save();
        //让Camera根据动画播放的时间在Z轴进行一定的偏移,使视图有远离视角的感觉。
        if (mReverse) {
            // z的偏移会越来越大。这就会形成这样一个效果,view从近到远
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            // z的偏移会越来越小。这就会形成这样一个效果,
            //我们的View从一个很远的地方向我们移过来,越来越近,最终移到了我们的窗口上面
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }
        //让视图围绕Y轴进行旋转,从而产生立体旋转的效果
        camera.rotateY(degrees);
        camera.getMatrix(matrix);
        camera.restore();

        //通过Matrix来确定旋转的中心点的位置。
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

applyTransformation方法在旋转动画过程中不断调用,

interpolatedTime取值范围【0,1】,从0到1取值

Rotate3dFrameLayout:3D布局

public class Rotate3dFrameLayout extends FrameLayout {

    private View childView1;
    private View childView2;
    private float mCenterX;
    private float mCenterY;
    private int Duration = 150;
    private boolean mIsFirst;

    public Rotate3dFrameLayout(@NonNull Context context) {
        this(context, null);
    }

    public Rotate3dFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Rotate3dFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //子View的数量
        int childCount = getChildCount();
        Log.e("111", "childCount=========" + childCount);
        if (childCount != 2) {
            throw new IllegalArgumentException("Rotate3dFrameLayout的子View数量必须为2");
        }

        childView1 = getChildAt(0);
        childView2 = getChildAt(1);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mCenterX = w / 2;
        mCenterY = h / 2;
        Log.e("111", "mCenterX=========" + mCenterX);
        Log.e("111", "mCenterY=========" + mCenterY);
    }

    public void applyRotation(boolean isFirst) {
        Rotate3dAnimation rotation = null;
        mIsFirst = isFirst;
        float mDepthZ = 300.0f;
        if (mIsFirst) {
            rotation = new Rotate3dAnimation(0, 90, mCenterX, mCenterY, mDepthZ, true);
        } else {
            rotation = new Rotate3dAnimation(0, -90, mCenterX, mCenterY, mDepthZ, true);
        }

        rotation.setDuration(Duration);
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateDecelerateInterpolator());
        rotation.setAnimationListener(new DisplayNextView());

        startAnimation(rotation);
    }

    private final class DisplayNextView implements Animation.AnimationListener {

        public void onAnimationStart(Animation animation) {
        }

        public void onAnimationEnd(Animation animation) {
            post(new SwapViews());
        }

        public void onAnimationRepeat(Animation animation) {
        }
    }

    private final class SwapViews implements Runnable {
        @Override
        public void run() {
            Rotate3dAnimation rotation = null;

            if (mIsFirst) {
                childView1.setVisibility(View.GONE);
                childView2.setVisibility(View.VISIBLE);
                rotation = new Rotate3dAnimation(-90, 0, mCenterX, mCenterY, 310.0f, false);
            } else {
                childView1.setVisibility(View.VISIBLE);
                childView2.setVisibility(View.GONE);
                rotation = new Rotate3dAnimation(90, 0, mCenterX, mCenterY, 310.0f, false);
            }

            rotation.setDuration(Duration);//设置动画持续时间
            rotation.setFillAfter(true);
            rotation.setInterpolator(new AccelerateDecelerateInterpolator());//设置动画变化速度

            startAnimation(rotation);
        }
    }

}

父布局指定只能正反两个子布局。

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.yxl.test.Rotate3dFrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00ff00">

        <TextView
            android:id="@+id/childView1"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:layout_margin="20dp"
            android:background="@drawable/bg1"
            android:gravity="center"
            android:text="红"
            android:textColor="#000000"
            android:textSize="45sp" />

        <TextView
            android:id="@+id/childView2"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:layout_margin="20dp"
            android:background="@drawable/bg2"
            android:gravity="center"
            android:text="蓝"
            android:textColor="#000000"
            android:textSize="45sp"
            android:visibility="gone" />

    </com.yxl.test.Rotate3dFrameLayout>

</LinearLayout>  

在Rotate3dFrameLayout中放置两个子布局:正反两面的两个布局。

MainActivityI


public class MainActivityI extends Activity {

    private Rotate3dFrameLayout container;
    private boolean isFirst = true;//默认第一面

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.rotate_layout);

        container = ((Rotate3dFrameLayout) this.findViewById(R.id.container));

        container.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                container.applyRotation(isFirst);
                isFirst = !isFirst;
            }
        });


    }
}

参考:Android立体旋转动画实现与封装(支持以X、Y、Z三个轴为轴心旋转)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值