android 3D过渡动画

参考:
Matrix的原理:http://blog.csdn.net/pathuang68/article/details/6991867

最近发现了一个谷歌的ApiDemos源码,感觉像是一本武林秘籍一样。。沉迷其中,不能自拔(呵呵,我不信~)
先看下实现的效果,36D过渡动画,咳咳,口误((●ˇ∀ˇ●)),3D过度动画,还是很酷炫的,接下来,就一步一步的分析吧。
这里写图片描述

android:persistentDrawingCache

详见:http://blog.csdn.net/love_xsq/article/details/43524103

首先介绍下ViewGroup的android:persistentDrawingCache属性
说明:设置ViewGroup的绘制缓存策略。保存缓存会消耗更多的内存,但可能会阻止频繁的垃圾回收是反复创建缓存。默认为PERSISTENT_SCROLLING_CACHE
可取的值:

  • PERSISTENT_NO_CACHE 说明不在内存中保存绘图缓存
  • PERSISTENT_ANIMATION_CACHE 说明只保存动画绘图缓存
  • PERSISTENT_SCROLLING_CACHE 说明只保存滚动效果绘图缓存
  • PERSISTENT_ALL_CACHES 说明所有的绘图缓存都应该保存在内存中

设置方式:

  • 代码里设置:(貌似不可以设置多个)
mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
  • xml中设置:(可取的值:none、animation、scorlling、all)
android:persistentDrawingCache="animation|scrolling"

layout animation

详见:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0619/3090.html

这里写图片描述
此属性也是ViewGroup的属性,先看下谷歌的注释

Sets the layout animation controller used to animate the group's  children after the first layout.

谷歌翻译:设置用于在第一次布局之后为viewgroup的子view设置动画的布局动画控制器。翻译的有点生硬,简单来说,就是控制子view的进场(第一次)动画
配置方式:

  • 1.xml配置:

viewgroup中加入此属性:

android:layoutAnimation="@anim/layout_bottom_to_top_slide"

layout_bottom_to_top_slide:

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/slide_right" />

其中:

  • android:delay表示动画播放的延时,既可以是百分比,也可以是float小数。eg:若动画时间为1秒,delay为0.2,子viewA先进,子viewB在A之后进入,A进入动画一秒,再等200毫秒后,B进入动画一秒
  • android:animationOrder表示动画的播放顺序,有三个取值normal(顺序)、reverse(反序)、random(随机)
  • android:animation指向了子控件所要播放的动画,这里暂不关注animation,下面会涉及

  • 2.代码配置:

        Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_right);//引用动画的资源
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.3f);//同上
        controller.setOrder(LayoutAnimationController.ORDER_REVERSE);//同上
        //设置插值器
        controller.setInterpolator(new DecelerateInterpolator());
        mPhotosList.setLayoutAnimation(controller);

translate

上文中的slide_right

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator">
    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromXDelta="-100%p"
        android:toXDelta="0" />
</set>

interpolator为修饰动画效果的插值器,可控制动画效果为线性、先快后慢、先慢后快等
其中translate的属性:

  • duration动画的持续时间
  • fromXDelta 属性为动画起始时 X坐标上的位置
  • toXDelta 属性为动画结束时 X坐标上的位置
  • fromYDelta 属性为动画起始时 Y坐标上的位置
  • toYDelta 属性为动画结束时 Y坐标上的位置

eg:android:toXDelta=”100%”,表示自身的100%,也就是从View自己的位置开始
android:toXDelta=”80%p”,表示父层View的80%,是以它父层View为参照的

Rotate3dAnimation

首先介绍下camera的坐标系

Camera的坐标系是左手坐标系。当手机平整的放在桌面上,X轴是手机的水平方向,Y轴是手机的竖直方向,Z轴是垂直于手机向里的那个方向。
终于到了,3d动画最核心的部分了
这里写图片描述
eg:camera.translate(10,50,-180)的意思是把观察物体右移(+x)10,上移(+y)50,向-z轴移180(即让物体接近camera,这样物体将会变大);
详见:http://www.jianshu.com/p/34e0fe5f9e31

Animation的applyTransformation方法

详见:https://www.cnblogs.com/kross/p/4087780.html
我也是从上文现学现卖,此方法大致概括为动画具体的实现,系统会以一个比较高的频率来调用这个方法

终于要说动画了吧,动画组成:

  • 上半部分动画: listview显示,imageview为gone,点击条目listview沿着Y轴从0度旋转到90度,旋转过程Z轴向屏幕朝里的方向移动,从0逐渐到一定深度
  • 转到90度节点时,listview为gone,imageview显示
  • 下半部分动画: imageview沿着Y轴从0度旋转到90度,旋转过程中Z轴向屏幕朝外的方向移动,从一定深度到0
    此3d动画为一个Animation的子类

分析的差不多了,上代码吧,里面注释还是挺多的。

/**
 * 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.
 * 沿着Y轴在两个指定角度之间旋转的动画。
 * 此动画还在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;

    /**
     * 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 3d旋转的X轴中心
     * @param centerY the Y center of the 3D rotation 3d旋转的Y轴中心
     * @param reverse true if the translation should be reversed, false otherwise
     *                为true,则从当前视角远离屏幕,为false,从一定的深度,靠近正常视角
     */
    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();
        //改变Z轴的值,若reverse为true,从正常视角,向屏幕朝里,逐渐改变深度
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }
        //沿着Y轴旋转
        camera.rotateY(degrees);
        camera.getMatrix(matrix);
        camera.restore();

        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

MainActivity

public class MainActivity extends AppCompatActivity implements
        AdapterView.OnItemClickListener, View.OnClickListener {
    private ListView mPhotosList;
    private ViewGroup mContainer;
    private ImageView mImageView;

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

        initView();

        initListener();
    }

    private void initListener() {
        mPhotosList.setOnItemClickListener(this);
        mImageView.setOnClickListener(this);
    }

    private void initView() {
        mPhotosList = findViewById(android.R.id.list);
        mImageView = findViewById(R.id.picture);
        mContainer = findViewById(R.id.container);

        final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, PHOTOS_NAMES);

        mPhotosList.setAdapter(adapter);

        mImageView.setClickable(true);
        mImageView.setFocusable(true);

        //设置布局时子view动画缓存策略
        mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
    }

    /**
     * 设置3d旋转
     *
     * @param position 根据position判断是要进行imageview(position=-1)的动画,还是listview的动画
     * @param start    旋转的起始角度
     * @param end      旋转的终止角度
     */
    private void applyRotation(int position, float start, float end) {
        // Find the center of the container
        final float centerX = mContainer.getWidth() / 2.0f;
        final float centerY = mContainer.getHeight() / 2.0f;

        //创建一个3d动画,远离屏幕
        final Rotate3dAnimation rotation =
                new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);
        rotation.setDuration(500);
        rotation.setFillAfter(true);
        //设置插值器,效果为逐渐加速
        rotation.setInterpolator(new AccelerateInterpolator());
        //设置监听是为了在动画完成后触发下一个动画
        rotation.setAnimationListener(new DisplayNextView(position));

        mContainer.startAnimation(rotation);
    }

    //点击listview的条目
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        // 先设置imageview的资源
        mImageView.setImageResource(PHOTOS_RESOURCES[position]);
        applyRotation(position, 0, 90);
    }

    //点击imageview
    public void onClick(View v) {
        applyRotation(-1, 180, 90);
    }

    /**
     * 监听动画结束,进行下半部分的动画
     */
    private final class DisplayNextView implements Animation.AnimationListener {
        private final int mPosition;

        private DisplayNextView(int position) {
            mPosition = position;
        }

        public void onAnimationStart(Animation animation) {
        }

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

        public void onAnimationRepeat(Animation animation) {
        }
    }

    /**
     * 开始下半部分的动画
     */
    private final class SwapViews implements Runnable {
        private final int mPosition;

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

        public void run() {
            final float centerX = mContainer.getWidth() / 2.0f;
            final float centerY = mContainer.getHeight() / 2.0f;
            Rotate3dAnimation rotation;

            if (mPosition > -1) {
                mPhotosList.setVisibility(View.GONE);
                mImageView.setVisibility(View.VISIBLE);
                mImageView.requestFocus();

                rotation = new Rotate3dAnimation(90, 180, centerX, centerY, 310.0f, false);
            } else {
                mImageView.setVisibility(View.GONE);
                mPhotosList.setVisibility(View.VISIBLE);
                mPhotosList.requestFocus();

                rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
            }

            rotation.setDuration(500);
            rotation.setFillAfter(true);
            //逐渐减速
            rotation.setInterpolator(new DecelerateInterpolator());

            mContainer.startAnimation(rotation);
        }
    }


    //照片名称
    private static final String[] PHOTOS_NAMES = new String[]{
            "Lyon",
            "Livermore",
            "Tahoe Pier",
            "Lake Tahoe",
            "Grand Canyon",
            "Bodie"
    };

    //照片资源
    private static final int[] PHOTOS_RESOURCES = new int[]{
            R.drawable.photo1,
            R.drawable.photo2,
            R.drawable.photo3,
            R.drawable.photo4,
            R.drawable.photo5,
            R.drawable.photo6
    };
}

翻转过后,图片其实是反过来的,修改翻转的起止角度就可以了(如可以把第一次的下半程动画角度修改为270-360度)
源码点我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值