Android动画--Activity界面180度翻转

Posted 买马小生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android动画--Activity界面180度翻转相关的知识,希望对你有一定的参考价值。

这个动画效果是把Activity当做一张纸,正反面都有内容,且当点击正反面的任何一个翻转按钮,Activity都会以屏幕中心为翻转中心点(Z轴的翻转中心点可以自由设定),进行旋转。

效果如下图所示(不懂上传Gif图,懂的人请告知哈):
正面
翻转(正面到反面)
翻转(正面到反面)
反面
翻转(反面到正面)
饭庄(反面到正面)
回到正面

这个动画效果的思路是这样的,首先两个界面的布局都在同一个Layout文件中,因为这里只有一个Activity,所以两个界面的布局在同一个layout文件中就要有所设计。
在这里,我使用的是FrameLayout作为父容器,包裹着两个RelativeLayout子容器。当然其他布局也可以,只是在初始化时候必须先设置不可见的一面的布局android:visibility=“gone”
布局文件具体代码如下:

<?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"
    >

    <FrameLayout
        android:id="@+id/framelayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--布局2 -->
        <RelativeLayout
            android:id="@+id/relative_layout_two"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:background="#50616d">

            <TextView
                android:id="@+id/tv_two"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="布局2"
                android:textColor="#789456"
                android:layout_centerInParent="true"
                android:textSize="30sp"
                android:visibility="gone"/>

            <Button
                android:id="@+id/rotate_back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="翻转"
                android:layout_centerInParent="true"
                android:layout_alignParentBottom="true"/>
        </RelativeLayout>

        <!--布局1 -->
        <RelativeLayout
            android:id="@+id/relative_layout_one"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:background="#0aa344">

            <TextView
                android:id="@+id/tv_one"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="布局1"
                android:textSize="30sp"
                android:textColor="#123456"
                android:visibility="visible"
                android:layout_centerInParent="true"/>

            <Button
                android:id="@+id/rotate_next"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="翻转"
                android:layout_centerInParent="true"
                android:layout_alignParentBottom="true"/>
        </RelativeLayout>

    </FrameLayout>
</LinearLayout>

注意,在两个RelativeLayout中必须添加android:focusable=”true”
android:focusableInTouchMode=”true”这两个属性来隔离两个界面的监听事件,使得当前显示的一面监听有效,另一个不显示的页面的监听无效,否则点击事件会乱套。

接下来就是翻转效果的设计,首先是确定翻转中心点的位置,通过传入X,Y,Z三个轴的中心点来确定。然后使用Camera根据传入的翻转度来进行翻转,翻转的类Rotate3D代码如下:

/**
 * 实现3D旋转动画核心类
 * Created by PawN on 2015/6/12.
 */
public class Rotate3D extends Animation{

    // 开始角度
    private final float mFromDegrees;
    // 结束角度
    private final float mToDegrees;
    // X轴中心点
    private final float mCenterX;
    // Y轴中心点
    private final float mCenterY;
    // Z轴中心点
    private final float mDepthZ;
    //是否需要扭曲
    private final boolean mReverse;
    //摄像头
    private Camera mCamera;

    public Rotate3D(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();
        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);
    }
}

接下来就是在TestRotateActivity中的使用了,整个翻转过程分为两个阶段操作,第一阶段是从当前页面翻转到整个Activity垂直于手机屏幕(到这个时刻,正反两面都是不可见的)。第二阶段是从当前的整个Activity垂直于手机屏幕(到这个时刻,正反两面都是不可见的)状态翻转到第二个页面显示在手机屏幕上。具体流程如下图所示:
翻转模拟图

TestRotateActivity的具体代码如下:

public class TestRotateActivity extends Activity{

    //布局1
    private TextView tvOne;
    private Button btnRotateNext;
    //布局2
    private TextView tvTwo;
    private Button btnRotateBack;


    //页面翻转容器FrameLayout
    private FrameLayout flContainer;
    //布局1界面RelativeLayout
    private RelativeLayout relativeLayout1;
    //布局2界面RelativeLayout
    private RelativeLayout relativeLayout2;
    //初始化界面索引(1位布局1,2位布局2)
    private int index = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_rotate);
        initView();
        registerClickListener();
    }

    private void initView(){
        tvOne = (TextView)findViewById(R.id.tv_one);
        tvTwo = (TextView)findViewById(R.id.tv_two);
        flContainer = 
(FrameLayout)findViewById(R.id.frame_layout);
        relativeLayout1 = (RelativeLayout)findViewById(R.id.relative_layout_one);
        relativeLayout2 = (RelativeLayout)findViewById(R.id.relative_layout_two);

        btnRotateNext = (Button) findViewById(R.id.rotate_next);
        btnRotateBack = (Button)findViewById(R.id.rotate_back);
    }

    private void registerClickListener(){
        tvOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(TestRotateActivity.this,"one",Toast.LENGTH_SHORT).show();;
            }
        });

        tvTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(TestRotateActivity.this,"two",Toast.LENGTH_SHORT).show();;
            }
        });
        btnRotateNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(index==1){
                    //第一阶段翻转
                    applyRotation(1,0,90);
                    index =0;
                }else{
                    //第一阶段翻转
                    applyRotation(0,0,-90);
                    index = 1;
                }
            }
        });
        btnRotateBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(index==1){
                    //第一阶段翻转
                    applyRotation(1,0,90);
                    index =0;
                }else{
                    //第一阶段翻转
                    applyRotation(0,0,-90);
                    index = 1;
                }
            }
        });
    }


    /**
     * 执行翻转第一阶段翻转动画
     * @param tag view索引
     * @param start 起始角度
     * @param end 结束角度
     */
    private void applyRotation(int tag, float start, float end) {
        // 得到中心点(以中心翻转)
        //X轴中心点
        final float centerX = flContainer.getWidth() / 2.0f;
        //Y轴中心点
        final float centerY = flContainer.getHeight() / 2.0f;
        //Z轴中心点
        final float depthZ = 500.0f;
        // 根据参数创建一个新的三维动画,并且监听触发下一个动画
        final Rotate3D rotation = new Rotate3D(start, end, centerX, centerY,depthZ, true);
        rotation.setDuration(300);//设置动画持续时间
        rotation.setInterpolator(new AccelerateInterpolator());//设置动画变化速度
        rotation.setAnimationListener(new DisplayNextView(tag));//设置第一阶段动画监听器
        flContainer.startAnimation(rotation);


    }

    /**
     * 第一阶段动画监听器
     *
     */
    private final class DisplayNextView implements Animation.AnimationListener {
        private final int tag;

        private DisplayNextView(int tag) {
           this.tag = tag;
        }

        public void onAnimationStart(Animation animation) {
        }

        public void onAnimationEnd(Animation animation) {
           //第一阶段动画结束时,也就是整个Activity垂直于手机屏幕,  
           //执行第二阶段动画
           flContainer.post(new SwapViews(tag));
           //调整两个界面各自的visibility
           adjustVisiable();
        }

        public void onAnimationRepeat(Animation animation) {
        }
    }

    /**
     * 执行翻转第二个阶段动画
     *
     */
    private final class SwapViews implements Runnable {
        private final int tag;

        public SwapViews(int position) {
           tag = position;
        }

        public void run() {
           if (tag == 0) {
              //首页页面以90~0度翻转
               showView(relativeLayout1, relativeLayout2, 90, 0);
           } else if (tag == 1) {
            //音乐页面以-90~0度翻转
            showView(relativeLayout2, relativeLayout1, -90, 0);
           }
        }
    }

    /**
     * 显示第二个视图动画
     * @param showView 要显示的视图
     * @param hiddenView 要隐藏的视图
     * @param startDegree 开始角度
     * @param endDegree 目标角度
     */
    private void showView(RelativeLayout showView, RelativeLayout hiddenView, int startDegree, int endDegree) {
        //同样以中心点进行翻转
        float centerX = showView.getWidth() / 2.0f;
        float centerY = showView.getHeight() / 2.0f;
        float centerZ = 500.0f;
        if (centerX == 0 || centerY == 0) {
            //调用该方法getMeasuredWidth(),必须先执行measure()方法,否则会出异常。
            showView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
            //获取该view在父容器里面占的大小
            centerX = showView.getMeasuredWidth() / 2.0f;
            centerY = showView.getMeasuredHeight() / 2.0f;
        }
//        Log.e("centerX",centerX + "");
//        Log.e("centerY",centerY + "");
        hiddenView.setVisibility(View.GONE);
        showView.setVisibility(View.VISIBLE);
        Rotate3D rotation = new Rotate3D(startDegree, endDegree, centerX, centerY, centerZ, false);
        rotation.setDuration(300);//设置动画持续时间
        rotation.setInterpolator(new DecelerateInterpolator());//设置动画变化速度
        flContainer.startAnimation(rotation);
    }

    /**
     * 两个布局的visibility调节
     */

    private void adjustVisiable(){
        if(tvOne.getVisibility() == View.VISIBLE){
            tvOne.setVisibility(View.GONE);
        }else {
            tvOne.setVisibility(View.VISIBLE);
        }
        if(tvTwo.getVisibility() == View.VISIBLE){
            tvTwo.setVisibility(View.GONE);
        }else {
            tvTwo.setVisibility(View.VISIBLE);
        }
    }
}

这样就可以实现本章的Activity翻转动画效果了~有哪里不妥的地方欢迎大家指正~

以上是关于Android动画--Activity界面180度翻转的主要内容,如果未能解决你的问题,请参考以下文章

android XML动画初步解析(activity界面之间跳转demo)

iOS翻转UIImageView 180度(非动画)

在颤动中旋转图标 180 度动画

[android] 界面切换的简单动画

多Activity应用

如何在画布内使用动画框架?