Android 补间动画

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_37482202/article/details/82944621

补间动画使用很简单,几行代码便能实现效果,上图上代码:

xml很简单,不用说,先给每个按钮加上点击监听

 findViewById(R.id.rotateBtn).setOnClickListener(new RotateButtonListener());
        findViewById(R.id.rotate3DBtn).setOnClickListener(new Rotate3DButtonListener());
        findViewById(R.id.scaleBtn).setOnClickListener(new ScaleButtonListener());
        findViewById(R.id.alphaBtn).setOnClickListener(new AlphaButtonListener());
        findViewById(R.id.translateBtn).setOnClickListener(new TranslateButtonListener());
        findViewById(R.id.groupBtn).setOnClickListener(new GroupButtonListener());
        findViewById(R.id.xmlBtn).setOnClickListener(new XMLButtonListener());

旋转动画:

RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)
RotateAnimation(float fromDegrees, float toDegrees)
public RotateAnimation(Context context, AttributeSet attrs)

RotateAnimation一共4种构造方法,前三种比较常用,最后一种是调用AnimationUtils.createAnimationFromXml或者AnimationUtils.loadAnimation才会用到,用于从xml创建动画,后面会演示

来讲一下前三种,参数解释代码注释里面我有写,这里说一下各自的默认值

private int mPivotXType = ABSOLUTE;
private int mPivotYType = ABSOLUTE;
private float mPivotXValue = 0.0f;
private float mPivotYValue = 0.0f;
private float mPivotX=0.0f;
private float mPivotY=0.0f;

 其它的动画大体相似,在此不再赘述

class RotateButtonListener implements View.OnClickListener {

        public void onClick(View v) {
            //参数1:从哪个旋转角度开始
            //参数2:转到什么角度
            //后4个参数用于设置围绕着旋转的圆的圆心在哪里
            //参数3:确定x轴坐标的类型,有ABSOLUT绝对坐标、RELATIVE_TO_SELF相对于自身坐标、RELATIVE_TO_PARENT相对于父控件的坐标
            //参数4:x轴的值,0.5f表明是以自身这个控件的一半长度为x轴
            //参数5:确定y轴坐标的类型
            //参数6:y轴的值,0.5f表明是以自身这个控件的一半长度为x轴
            RotateAnimation rotateAnimation = new RotateAnimation(0, 720,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            rotateAnimation.setDuration(2000);
            v.startAnimation(rotateAnimation);
        }
    }

缩放动画:

class ScaleButtonListener implements View.OnClickListener {

        public void onClick(View v) {
            //参数1:x轴的初始值
            //参数2:x轴收缩后的值
            //参数3:y轴的初始值
            //参数4:y轴收缩后的值
            //参数5:确定x轴坐标的类型
            //参数6:x轴的值,0.5f表明是以自身这个控件的一半长度为x轴
            //参数7:确定y轴坐标的类型
            //参数8:y轴的值,0.5f表明是以自身这个控件的一半长度为x轴
            ScaleAnimation scaleAnimation = new ScaleAnimation(
                    0, 1.5f, 0, 1.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            scaleAnimation.setDuration(2000);
            v.startAnimation(scaleAnimation);
        }
    }

透明度动画:

class AlphaButtonListener implements View.OnClickListener {

        public void onClick(View v) {
            //创建一个AlphaAnimation对象,参数从完全的透明度,到完全的不透明
            AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
            //设置动画执行的时间
            alphaAnimation.setDuration(2000);
            //将alphaAnimation对象添加到AnimationSet当中
            //使用ImageView的startAnimation方法执行动画
            v.startAnimation(alphaAnimation);
        }
    }

平移动画:

class TranslateButtonListener implements View.OnClickListener {

        public void onClick(View v) {
            //参数1~2:x轴的开始位置
            //参数3~4:X轴的结束位置
            //参数5~6:Y轴的开始位置
            //参数7~8:Y轴的结束位置
            TranslateAnimation translateAnimation = new TranslateAnimation(
                            Animation.RELATIVE_TO_SELF, 0f,
                            Animation.RELATIVE_TO_SELF, 2f,
                            Animation.RELATIVE_TO_SELF, 0f,
                            Animation.RELATIVE_TO_SELF, 0f);
            translateAnimation.setDuration(2000);
            v.startAnimation(translateAnimation);
        }
    }

组合动画:

private class GroupButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            //传true代表使用系统默认的interpolator,传false代表使用自己自定义的interpolator
            AnimationSet animationSet=new AnimationSet(true);
            TranslateAnimation translateAnimation = new TranslateAnimation(
                    Animation.RELATIVE_TO_SELF, -2f,
                    Animation.RELATIVE_TO_SELF, 0f,
                    Animation.RELATIVE_TO_SELF, 0f,
                    Animation.RELATIVE_TO_SELF, 0f);
            translateAnimation.setDuration(2000);
            AlphaAnimation alphaAnimation=new AlphaAnimation(0,1);
            alphaAnimation.setDuration(2000);
            animationSet.addAnimation(translateAnimation);
            animationSet.addAnimation(alphaAnimation);
            v.startAnimation(animationSet);
        }
    }

XML动画:

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    //下方属性为四种动画共有属性
    android:duration="3000"  //动画持续时间
    android:startOffset="1000"  //延持执行时间
    android:fillBefore="true"  //动画执行完后是否停留在初始状态 默认true
    android:fillAfter="false"  //动画执行完后是否停留在最后状态 默认false 优先于fillBefore
    android:fillEnabled="true"  //fillBefore属性是否可用 默认为true
    android:repeatMode="restart"  //动画播放模式 restart是顺序播放 reverse为倒序播放 默认正序
    android:repeatCount = “0” // 重放次数(动画的播放次数=重放次数+1),为infinite时无限重复 

    //translate动画特有属性
    android:fromXDelta="0"  //X起始坐标
    android:toXDelta="500"  //X终点坐标
    android:fromYDelta="0"  //Y起始坐标
    android:toYDelta="500"  //Y终点坐标
    />
private class XMLButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            Animation animation = AnimationUtils.loadAnimation(AnimationActivity.this, R.anim.translate);
            v.startAnimation(animation);
        }
    }

其它动画的xml动画大体相似,这里说几个需要注意的地方

缩放动画:

fromXScale fromYScale 动画在X和Y轴上的起始缩放倍数 ,1.0表示正常大小,比1大是放大,比1小是缩小, 0.0表示收缩到没有

privotX prrvotY 缩放轴点的X,Y坐标

设置是数字时(如50),轴点是在左上角原点X或Y方向加上该50px的点

设置是百分比时(如50%),轴点是在左上角该视图50%宽度或高度距离的点

设置为百分之P时(如50%p),轴点是在左上角该视图的父视图50%宽度或高度的点

可以使用xml写组合动画,使用set标签,默认是一起播放,如果想设置播放顺序可以使用startoffset

我们除了可以给视图加动画,还可以使用补间动画来切换跳转界面或者fragment

left_slide_out.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0"
    android:toXDelta="-100%"
    android:duration="1000"
    />

right_slide_in.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="100%"
    android:toXDelta="0"
    android:duration="1000"
    />
overridePendingTransition(R.anim.right_slide_in,R.anim.left_slide_out);
startActivity(new Intent(AnimationActivity.this,Activity2.class));

我们使用v.startAnimation()来开始动画,那它是如何执行的呢?让我们来看看源码

startAnimation()方法

/**
     * Start the specified animation now.
     *
     * @param animation the animation to start now
     */
    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
        setAnimation(animation);
        invalidateParentCaches();
        invalidate(true);
    }

 第一行是设置动画开始时间,START_ON_FIRST_FRAME代表立刻执行,也可以调用start()方法,两个方法一样

/**
     * Convenience method to start the animation the first time
     * {@link #getTransformation(long, Transformation)} is invoked.
     */
    public void start() {
        setStartTime(-1);
    }

第二行是设置动画,其中包含了判断屏幕状态,如果屏幕是灭的,那么亮屏才会执行

public void setAnimation(Animation animation) {
        mCurrentAnimation = animation;

        if (animation != null) {
            // If the screen is off assume the animation start time is now instead of
            // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
            // would cause the animation to start when the screen turns back on
            if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF
                    && animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
                animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
            }
            animation.reset();
        }
    }

 第三行是用于移除其父控件的PFLAG_INVALIDATED标签,这样其父控件就会重建用于渲染的显示列表,该方法不会引起invalidate方法的调用,所以第四行需要调用一遍invalidate方法

 /**
     * Used to indicate that the parent of this view should clear its caches. This functionality
     * is used to force the parent to rebuild its display list (when hardware-accelerated),
     * which is necessary when various parent-managed properties of the view change, such as
     * alpha, translationX/Y, scrollX/Y, scaleX/Y, and rotation/X/Y. This method only
     * clears the parent caches and does not causes an invalidate event.
     *
     * @hide
     */
    protected void invalidateParentCaches() {
        if (mParent instanceof View) {
            ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
        }
    }

 既然知道了动画是如何开始执行的了,我们可以自己尝试调用一次

            rotateAnimation.start();
            v.setAnimation(rotateAnimation);
            Class<?> class1 = v.getClass();
            try{
                Method method = class1.getSuperclass().getSuperclass().getDeclaredMethod("invalidateParentCaches");
                method.setAccessible(true);
                method.invoke(v);
            }catch (Exception e){
                e.printStackTrace();
            }
            v.invalidate();

 首先调用start()方法设置动画开始时间,设置动画,随后使用反射调用invalidateParentCaches方法重建渲染列表,这块我遇到了两个小问题。

第一个问题是protected的权限问题,被protected修饰的方法可以被同包下的类或子类调用。Button是View的子类,却不是一个包下,这种情况下该方法只能在子类内部调用却不能被子类对象调用,这点需要注意一下。

第二个问题是反射调用的问题,首先需要明确getDeclardMethod和getMethod两个方法的区别。getDeclaredMethod可以获取对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问、私有方法和它所实现接口的方法,但不包括继承的方法。getMethod方法返回某个类的所有公用(public)方法包括其继承类的公用方法和它所实现接口的方法。所以说前者比后者获取的范围大,但是不包括继承的方法,所以要想从一个Button对象获取到View对象的方法需要调用两次getSuperclass。Button类的父类是TextView,TextView的父类才是View

解决了这个问题后,调用invalidate更新视图,动画便执行了

展开阅读全文

没有更多推荐了,返回首页