视图动画(补间)
以下为Android常用的视图动画类,xml动画这里不做详解。
基本动画
-
ScaleAnimation(缩放动画)可变化控件的大小
/** * Scale动画里x、Y指这个控件的宽高百分比,取值0~1 * * @param fromX 动画开始的X。 * @param toX 动画结束的X。 * @param fromY 动画开始的y。 * @param toY 动画结束的y。 * * @param 动画开始时X坐标类型,可以理解为从控件的哪个位置开始,下方动画同值。 * 取值范围为ABSOLUTE(绝对位置)、RELATIVE_TO_SELF(以自身宽或高为参考)、 * RELATIVE_TO_PARENT(以父控件宽或高为参考)。 * @param pivotXValue 取值0~1(1 is 100%) * @param pivotYType 动画开始时坐标类型 * 取值范围为ABSOLUTE(绝对位置)、RELATIVE_TO_SELF(以自身宽或高为参*考)、 * RELATIVE_TO_PARENT(以父控件宽或高为参考)。 * @param pivotYValue 取值0~1(1 is 100%) * 下面代码动画表示:从控件的左上角位置开始,放大1.5倍 */ val animation2 = ScaleAnimation(1f, 1.5f, 1f, 1.5f, ScaleAnimation.RELATIVE_TO_SELF, 0f, ScaleAnimation.RELATIVE_TO_SELF, 0f) animation2.duration = 700
-
RotateAnimation
/** * 旋转动画 * @param fromDegrees 开始前角度 0代表当前无角度 * @param toDegrees 动画结束角度 * @param pivotXType * @param pivotXValue * @param pivotYType * @param pivotYValue * 下面代码动画表示:从控件的中心旋转360° */ val animation3 = RotateAnimation(0f, 360f, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f)
-
TranslateAnimation
/** * 平移动画 * @param fromXDelta 动画前X坐标 * @param toXDelta 动画结束X坐标 * @param fromYDelta 动画前Y * @param toYDelta 动画结束Y坐标 * 下面代码动画表示:从当前控件的起始位置0,向上Y移动100像素 */ var animation1 = TranslateAnimation(0f, 0f, 0f, -100f)
-
AlphaAnimation
/** * 透明度动画 * @param fromAlpha 动画前View的通明度 * @param toAlpha 动画结束时View的通明度 */ val animation4 = AlphaAnimation(1f, 0.1f)
进阶用法
AnimationSet组合动画,对View运行组合动画,这里需要注意AnimationSet中添加的动画是一起执行的,不能设定动画的先后执行顺序,同样也不能在动画的过程中进行操作。
/**
* AnimationSet 有两个构造函数
* AnimationSet(Context context, AttributeSet attrs) // 传入一组动画属性attr(执行时间等)
* AnimationSet(boolean shareInterpolator) // 是否共用插值器
* 注意事项:
* 1.AnimationSet设定duration值(执行时长),会使集合中animation duration属性值失效
* 2.AnimationSet设定repeatCount值(重复次数)无效,只会取子动画设定值
* 3.AnimationSet设定repeatMode值(REVERSE从结束位置执行反动画,RESTART重新执行动画),会使子动画repeatMode无效
*/
/** AnimationSet源码
*/
class AnimationSet extends Animation {
@Override
public long getDuration() {
final ArrayList<Animation> animations = mAnimations;
final int count = animations.size();
long duration = 0;
boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
if (durationSet) { //如果AnimationSet有设置duration,则取AnimationSet的duration
duration = mDuration;
} else { // 取子动画中最大的duration
for (int i = 0; i < count; i++) {
duration = Math.max(duration, animations.get(i).getDuration());
}
}
return duration;
}
}
//使用方式
val animationTest = AnimationSet(true)
animationTest.addAnimation(animation1)
animationTest.addAnimation(animation2)
animationTest.addAnimation(animation3)
animationTest.addAnimation(animation4)
animationTest.duration = 900
animationTest.interpolator = LinearInterpolator() //线性插值器
视图动画原理解析
/**
* view.startAnimation(animation) view与animation产生联系
* 初始化动画的启动设定为第一祯,调用reset方法,也就是说不管动画是否在执行过程,再次执行动画依然回到第一 * 帧。接着invalidate开始重绘,UI重绘机制这里不做详解,我们直接找到getAnimation()调用的地方 * view.draw(Canvas canvas, ViewGroup parent, long drawingTime)
*/
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
public void setAnimation(Animation animation) {
mCurrentAnimation = animation;
if (animation != null) {
if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF
&& animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
animation.reset();
}
}
/**View类*/
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
// ...省略代码
final Animation a = getAnimation();
if (a != null) {
//处理视图活动Animation
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
}
}
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
//..
final Transformation t = parent.getChildTransformation();
boolean more = a.getTransformation(drawingTime, t, 1f);
if (more) {
// 判断动画是否过期,如果没有过期,则去执行parent的invalidate,会重新执行child重绘
// 绘制下一帧动画,16ms一帧
parent.invalidate(left, top, left + (int) (region.width() + .5f),
top + (int) (region.height() + .5f));
}
}
}
/**Animatio类*/
public abstract class Animation implements Cloneable {
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {
mStartTime = currentTime;
}
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
if (duration != 0) {
// 先初始化动画的启动进度,比如1个4s的动画,计算现在动画进度百分比
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
//判断动画是否过期
final boolean expired = normalizedTime >= 1.0f || isCanceled();
mMore = !expired;
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
if (!mStarted) {
fireAnimationStart();
mStarted = true;
if (NoImagePreloadHolder.USE_CLOSEGUARD) {
guard.open("cancel or detach or getTransformation");
}
}
if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if (mCycleFlip) {
normalizedTime = 1.0f - normalizedTime;
}
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime); // 通过插值器计算出平稳值
//执行动画,调用子类ScaleAnimation、RotateAnimation的实现方法,这里说明我们也可以自定义Animation,通过重写applyTransformation方法,执行自己的动画逻辑
applyTransformation(interpolatedTime, outTransformation);
}
if (expired) {
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
// ... 动画结束
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
// 判断动画RepeatCount,是否重复执行
fireAnimationRepeat();
}
}
return mMore;
}
}
视图动画的局限性
- 只能作用于view,但有时需求不是对于整个view的,而只是对view的某个属性的,例如颜色的变化,也无法对非View的对象进行动画处理。
- 只改变了view的视觉效果而已,修改了视图绘制的地方,例如控件的点击,还是动画前控件的位置。
- 动画效果固定,动画类型只有四种,缩放,平移,旋转,透明度的基本动画,无法对其他属性进行操作。
- 动画虽然可以添加监听,但是动画开始后无法对动画的执行过程进行控制。
视图动画的优点
- 使用方便,能满足基础动画效果