关于Android中动画探究(一)——视觉动画

mac2025-10-06  1

文章目录

视图动画(补间)基本动画进阶用法视图动画原理解析视图动画的局限性视图动画的优点

视图动画(补间)

以下为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的视觉效果而已,修改了视图绘制的地方,例如控件的点击,还是动画前控件的位置。动画效果固定,动画类型只有四种,缩放,平移,旋转,透明度的基本动画,无法对其他属性进行操作。动画虽然可以添加监听,但是动画开始后无法对动画的执行过程进行控制。

视图动画的优点

使用方便,能满足基础动画效果
最新回复(0)