《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的属性,这是补间动画的不足,而属性动画的出现,就弥补了这个不足。