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

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

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

文章目录

  • 补间动画
    • 使用方法一:xml
      • translate(平移动画)
      • rotate(旋转动画)
      • scale(缩放动画)
      • alpha(透明度动画)
      • set(组合动画)
      • 属性及含义
    • 实现方法二:java代码

补间动画

补间动画也称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" />

Android自定义控件入门到精通--补间动画_第1张图片

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

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

百分比

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

Android自定义控件入门到精通--补间动画_第2张图片

动画从相对于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" />

Android自定义控件入门到精通--补间动画_第3张图片

动画从相对于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"
    />

Android自定义控件入门到精通--补间动画_第4张图片

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

Android自定义控件入门到精通--补间动画_第5张图片

百分比

<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%"
    />

Android自定义控件入门到精通--补间动画_第6张图片

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

Android自定义控件入门到精通--补间动画_第7张图片

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

实现方法二:java代码

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

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

Android自定义控件入门到精通--补间动画_第8张图片

(图片只截了一半,知道中心位置在父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自定义控件入门到精通--补间动画_第9张图片

  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);

        }
    });
}

Android自定义控件入门到精通--补间动画_第10张图片

总之,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设置点击事件,然后通过补间动画对它进行平移,看看点击事件是在开始位置还是结束位置能触发

Android自定义控件入门到精通--补间动画_第11张图片

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

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

你可能感兴趣的:(android)