参考:
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;
}
});
}
}