Android自定义控件入门到精通--补间动画

《Android自定义控件入门到精通》文章索引 ☞ https://blog.csdn.net/Jhone_csdn/article/details/118146683

《Android自定义控件入门到精通》所有源码 ☞ https://gitee.com/zengjiangwen/Code

补间动画

补间动画也称TweenAnimation/ViewAnimation,补间动画为View的两种状态间的过度动画,但只是视觉上的改变,并没有真正改变View本身的属性。

补间动画有以下几种

  • TranslateAnimation(平移动画)
  • RotateAnimation(旋转动画)
  • ScaleAnimation(缩放动画)
  • AlphaAnimation(透明度动画)
  • AnimationSet(组合动画)

使用方法一:xml

步骤一:编写动画xml

translate(平移动画)

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:fillAfter="true"
    android:toXDelta="100"
    android:toYDelta="0" />

rotate(旋转动画)

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:fillAfter="true"
    android:fromDegrees="0"
    android:toDegrees="60"
    android:pivotX="50%"
    android:pivotY="50%"
    />

scale(缩放动画)

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:fillAfter="true"
    android:fromXScale="1"
    android:fromYScale="1"
    android:toXScale="2"
    android:toYScale="2"
    android:pivotX="50%"
    android:pivotY="50%"
    />

alpha(透明度动画)

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:fillAfter="true"
    android:fromAlpha="1"
    android:toAlpha="0"
    />

set(组合动画)

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fillAfter="true">
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toXDelta="200"
        android:toYDelta="0" />
    <scale
        android:fromXScale="0"
        android:fromYScale="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1"
        android:toYScale="1" />
    <rotate
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="90" />
    <alpha
        android:fromAlpha="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />
</set>

步骤二:Activity

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final View testView=findViewById(R.id.testView);
        Button playBtn=findViewById(R.id.playBtn);
        Button stopBtn=findViewById(R.id.stopBtn);
        final Animation animation = AnimationUtils.loadAnimation(this, R.anim.traslate);
        playBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testView.startAnimation(animation);
            }
        });
        stopBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testView.clearAnimation();
            }
        });
    }

或者这样调用

final Animation animation = AnimationUtils.loadAnimation(this, R.anim.traslate);
testView.setAnimation(animation);
playBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        animation.start();
    }
});
stopBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        animation.cancel();
    }
});

属性及含义

属性名称含义
duration动画开始到结束的时间
fromXDelta动画起始的x轴位置(相对于View自己的坐标系,View自己左上角为(0,0))
toXDelta动画结束的x轴位置
fromYDelta动画起始的y轴位置
toYDelta动画结束的y轴位置
repeatMode动画的重复模式,需要配合repeatCount才有效果,reverse:往返重复,restart:每次结束后从开始位置开始重复
repeatCount重复次数,具体数字(无限重复为infinite)
interpolator动画插值器
fillAfter是否保持动画结束时的状态
pivotX原点位置(动画参考点,只对rotate和scale动画起作用)
pivotY原点位置(动画参考点,只对rotate和scale动画起作用)
fromDegrees旋转的开始角度
toDegrees旋转的结束角度
fromXScale/fromYScale缩放的开始大小(倍速关系,例:为2时表示开始大小为2倍大小)
toXScale/toYScale缩放的结束大小
fromAlpha透明度改变前的值([0~1],为0表示全透明,看不见)
toAlpha透明度改变后的值

比较简单,这里有几个属性的用法需要注意下:fromXDelta、toXDelta、fromYDelta、toYDelta、pivotX、pivoY

这些可以填数值(100),百分比(100%),百分比P(100%p),我们来演示下,他们有什么不同。

fromXDelta、toXDelta、fromYDelta、toYDelta

我们拿fromYDelta举例:

数值

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromYDelta="100"
    android:toYDelta="0" />

在这里插入图片描述

动画从相对于TextView布局位置100px距离开始做动画

(白色TextView表示初始的布局位置,红色区域为TextView的父布局大小,蓝色为演示动画)

百分比

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromYDelta="100%"
    android:toYDelta="0" />

在这里插入图片描述

动画从相对于TextView布局位置100%距离开始做动画(也就是TextView的高度距离)

百分比P(P=Parent)

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromYDelta="100%p"
    android:toYDelta="0" />

在这里插入图片描述

动画从相对于TextView父布局位置100%距离开始做动画(也就是TextView父布局的高度距离)

pivotX、pivoY(原点位置)(数值、百分比、百分比P)

数值

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromDegrees="0"
    android:toDegrees="90"
    android:pivotY="30"
    android:pivotX="30"
    />

在这里插入图片描述

围绕(30,30)这个原点旋转

在这里插入图片描述

百分比

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromDegrees="0"
    android:toDegrees="90"
    android:pivotY="50%"
    android:pivotX="50%"
    />

在这里插入图片描述

50%为View宽高的一半,动画围绕View中心点旋转

百分比P

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromDegrees="0"
    android:toDegrees="90"
    android:pivotY="50%p"
    android:pivotX="50%p"
    />

在这里插入图片描述

50%p为Parent宽高的一半,动画围绕Parent中心点旋转

实现方法二:java代码

和帧动画一样,除了可以使用xml定义使用动画,也可以用java代码实现,并且java代码实现更灵活强大。

比如下面我要平移View,使View的中心点和Parent的中心点重合,xml的方式是无法实现的

在这里插入图片描述

(图片只截了一半,知道中心位置在父View的中心就行)

有人会有疑问了,不会吧,我这样定义动画应该就可以啊:

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXDelta="50%p"
    android:toYDelta="50%p" />

那我们看看结果,现实总是出乎预料

在这里插入图片描述

  android:pivotX="50%"
  android:pivotY="50%"

这两个定义原点的属性并没有起作用,注意了,这两个是rotate和scale动画的属性,对translate是不起作用的,对于translate来说,它的原点就是View的左上角,且不能修改

那我们通过java代码的方式,就能算出结束时候的原点坐标,实现这个效果

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

    final View testView = findViewById(R.id.testView);
    Button playBtn = findViewById(R.id.playBtn);
    playBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int width = testView.getWidth();
            int height = testView.getHeight();

            int parentWidth = ((ViewGroup) testView.getParent()).getWidth();
            int parentHeight = ((ViewGroup) testView.getParent()).getHeight();

            int toXValue=(parentWidth-width)/2;
            int toYValue=(parentHeight-height)/2;

            final TranslateAnimation animation = new TranslateAnimation(Animation.ABSOLUTE,0,Animation.ABSOLUTE,toXValue,Animation.ABSOLUTE,0,Animation.ABSOLUTE,toYValue);
            animation.setDuration(2000);
            animation.setFillAfter(true);

            testView.startAnimation(animation);

        }
    });
}

在这里插入图片描述

总之,java代码实现根xml差不多,不同之处就是它可以根据需求计算位置,对应的类为:

  • TranslateAnimation
  • RotateAnimation
  • ScaleAnimation
  • AlphaAnimation
  • AnimationSet

我们讲解下TranslateAnimation,其它的就会用了

TranslateAnimation的构造函数

public TranslateAnimation(Context context, AttributeSet attrs)
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
        int fromYType, float fromYValue, int toYType, float toYValue)

第一种构造方法就不多讲了,不多用,我们看看第二种:

public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
    mFromXValue = fromXDelta;
    mToXValue = toXDelta;
    mFromYValue = fromYDelta;
    mToYValue = toYDelta;

    mFromXType = ABSOLUTE;
    mToXType = ABSOLUTE;
    mFromYType = ABSOLUTE;
    mToYType = ABSOLUTE;
}
  TranslateAnimation translateAnimation=new TranslateAnimation(0,300,0,300);

这种方式跟我们xml使用数值的效果是一样的:

<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="300"
    android:toYDelta="300" />

第三种构造方法:

public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
        int fromYType, float fromYValue, int toYType, float toYValue) {

    mFromXValue = fromXValue;
    mToXValue = toXValue;
    mFromYValue = fromYValue;
    mToYValue = toYValue;

    mFromXType = fromXType;
    mToXType = toXType;
    mFromYType = fromYType;
    mToYType = toYType;
}

细心的朋友应该发现了,第二种构造方法其实跟第三种构造方法是一样的,只不过它的mFromXType 、mToXType 、mFromYType 、mToYType 都写定了ABSOLUTE(默认)

type有三种:

  • ABSOLUTE(绝对,相对布局位置,相当于xml中的数值
  • RELATIVE_TO_SELF(相对自己,相当于xml中的百分比
  • RELATIVE_TO_PARENT(相对父控件,相当于xml中的百分比P

ABSOLUTE

new TranslateAnimation(Animation.ABSOLUTE,0,Animation.ABSOLUTE,300,Animation.ABSOLUTE,0,Animation.ABSOLUTE,300);
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="300"
    android:toYDelta="300" />

这两种写法意思是一样的

RELATIVE_TO_SELF

new TranslateAnimation(Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0.5f,
                       		Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0.5f);
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="50%"
    android:toYDelta="50%" />

这两种写法意思是一样的,注意0.5f是倍数的意思

RELATIVE_TO_PARENT

new TranslateAnimation(Animation.RELATIVE_TO_PARENT,0,Animation.RELATIVE_TO_PARENT,0.5f,
							Animation.RELATIVE_TO_PARENT,0,Animation.RELATIVE_TO_PARENT,0.5f);
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:fromXDelta="0"
    android:fromYDelta="0"
    android:toXDelta="50%p"
    android:toYDelta="50%p" />

这两种写法意思是一样的,0.5f是倍数,相对于父View的倍数

我们看到第三种构造函数参数是fromXValue而非fromXDelta这样的,他们之间有什么转化关系呢?

//TranslateAnimation.java
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
    super.initialize(width, height, parentWidth, parentHeight);
    mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth);
    mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);
    mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);
    mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);
}

mFromXValue通过resolveSize()转化成mFromXDelta

//Animation.java
protected float resolveSize(int type, float value, int size, int parentSize) {
    switch (type) {
        case ABSOLUTE:
            return value;
        case RELATIVE_TO_SELF:
            return size * value;
        case RELATIVE_TO_PARENT:
            return parentSize * value;
        default:
            return value;
    }
}

补间动画是一种假动画,它是一种视觉效果,实际并没有改变View的实际位置、大小、透明度,比如我们给View设置点击事件,然后通过补间动画对它进行平移,看看点击事件是在开始位置还是结束位置能触发

在这里插入图片描述

可以看到,我们点击动画结束后的位置,View并没有响应点击事件,反而在动画开始的位置点击,才响应点击事件。

补间动画并不能真正改变View的属性,这是补间动画的不足,而属性动画的出现,就弥补了这个不足。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一鱼浅游

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值