指由许多帧静止的画面,以一定的速度(如每秒16张)连续播放时,肉眼因视觉残象产生错觉,而误以为画面活动的作品。。这是wiki百科的定义,但在实际开发中,手机是以每秒60帧进行绘制元素,保证动画的流畅度。
官方定义:加载一系列的Drawable,一个接着另一个,组成动画
xml:
<animation-list>
item 元素的android:duration属性设置播放的时间,单位为毫秒(ms)
java: android.graphics.drawable.AnimationDrawable view.setBackgroundResource(R.drawable.animlist);
1.构造方法,默认构造方法没有state,res为null
private AnimationDrawable(AnimationState state, Resources res) {
final AnimationState as = new AnimationState(state, this, res);
setConstantState(as);
if (state != null) {
setFrame(0, true, false);
}
}
android.graphics.drawable.AnimationDrawable.AnimationState 继承android.graphics.drawable.DrawableContainer.DrawableContainerState AnimationState构造方法主要是对内部对象android.graphics.drawable.DrawableContainer.DrawableContainerState#mOwner的拷贝
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
setConStatState方法将as保持到对象mAnimationState。
2.addFrame方法,将当前的frame保存到mAnimationState。并把frame设置为invisable
public void addFrame(@NonNull Drawable frame, int duration) {
mAnimationState.addFrame(frame, duration);
if (!mRunning) {
setFrame(0, true, false);
}
}
android.graphics.drawable.AnimationDrawable.AnimationState#addFrame
public void addFrame(Drawable dr, int dur) {
int pos = super.addChild(dr);
mDurations[pos] = dur;
}
super.addChild(dr),保存了drawable并设置为不可见,保存drawable是个数组,每次容器大小不够,用System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize);增长10个单位。
接下来看下AnimationDrawable的setFrame方法
private void setFrame(int frame, boolean unschedule, boolean animate) {
if (frame >= mAnimationState.getChildCount()) {
return;
}
mAnimating = animate;
mCurFrame = frame;
selectDrawable(frame);
if (unschedule || animate) {
unscheduleSelf(this);
}
if (animate) {
// Unscheduling may have clobbered these values; restore them
mCurFrame = frame;
mRunning = true;
scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
}
}
selectDrawable根据上面的addFrame方法,可知frame为0,unscheduleSelf方法调用Drawable的消息回调接口。
- start方法
public void start() {
mAnimating = true;
if (!isRunning()) {
// Start from 0th frame.
setFrame(0, false, mAnimationState.getChildCount() > 1
|| !mAnimationState.mOneShot);
}
}
setFrame可以看,步骤2的分析。调用了android.graphics.drawable.Drawable#scheduleSelf方法,最终执行android.graphics.drawable.AnimationDrawable#run方法。run方法只有一行代码nextFrame(false),如下代码块。nextFrame增加1,再循环下一帧。
private void nextFrame(boolean unschedule) {
int nextFrame = mCurFrame + 1;
final int numFrames = mAnimationState.getChildCount();
final boolean isLastFrame = mAnimationState.mOneShot && nextFrame >= (numFrames - 1);
// Loop if necessary. One-shot animations should never hit this case.
if (!mAnimationState.mOneShot && nextFrame >= numFrames) {
nextFrame = 0;
}
setFrame(nextFrame, unschedule, !isLastFrame);
}
官方定义:使用视图动画系统(view animation system)执行View的补间动画。补间通过计算start point, end point, size, rotation的信息执行动画
xml:
<alpha>, <scale>, <translate>, <rotate>,<set>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/list_item_alpha"
android:animationOrder="normal"
android:delay="0.8" />
java:
android.view.animation.AnimationSet, android.view.animation.Animation, android.view.animation.AnimationUtils,
android.view.animation.LayoutAnimationController 实现使多个控件按顺序一个一个的显示。
android.view.animation.Animation
- AlphaAnimation构造方法
public AlphaAnimation(float fromAlpha, float toAlpha) {
mFromAlpha = fromAlpha;
mToAlpha = toAlpha;
}
- start方法
调用android.view.animation.Animation#setStartTime方法
/**
* When this animation should start. When the start time is set to
* {@link #START_ON_FIRST_FRAME}, the animation will start the first time
* {@link #getTransformation(long, Transformation)} is invoked. The time passed
* to this method should be obtained by calling
* {@link AnimationUtils#currentAnimationTimeMillis()} instead of
* {@link System#currentTimeMillis()}.
*
* @param startTimeMillis the start time in milliseconds
*/
public void setStartTime(long startTimeMillis) {
mStartTime = startTimeMillis;
mStarted = mEnded = false;
mCycleFlip = false;
mRepeated = 0;
mMore = true;
}
注释提示会调用#getTransformation(long, Transformation),在View.Draw时候的执行android.view.View#applyLegacyAnimation会调用到
draw()绘制的元素
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
applyLegacyAnimation方法
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
Transformation invalidationTransform;
final int flags = parent.mGroupFlags;
final boolean initialized = a.isInitialized();
if (!initialized) {
a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
onAnimationStart();
}
final Transformation t = parent.getChildTransformation();
boolean more = a.getTransformation(drawingTime, t, 1f);
if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
if (parent.mInvalidationTransformation == null) {
parent.mInvalidationTransformation = new Transformation();
}
invalidationTransform = parent.mInvalidationTransformation;
a.getTransformation(drawingTime, invalidationTransform, 1f);
} else {
invalidationTransform = t;
}
a.setListenerHandler(mAttachInfo.mHandler);设置了Animation的handler,进行事件处理
可以看出是运用Transform进行对象的设置改变的
android.view.animation.AlphaAnimation#applyTransformation
/**
* Changes the alpha property of the supplied {@link Transformation}
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}
先看下android.view.animation.Transformation含有两个重要属性
protected Matrix mMatrix;
protected float mAlpha;
这两个属性就是属性动画包换的视图转换信息
- cancel方法
public void cancel() {
if (mStarted && !mEnded) {
fireAnimationEnd();
mEnded = true;
guard.close();
}
// Make sure we move the animation to the end
mStartTime = Long.MIN_VALUE;
mMore = mOneMoreTime = false;
}
public static Animation loadAnimation(Context context, @AnimRes int id)
throws NotFoundException {
XmlResourceParser parser = null;
try {
parser = context.getResources().getAnimation(id);
return createAnimationFromXml(context, parser);
最终调用android.view.animation.AnimationUtils#createAnimationFromXml(android.content.Context, org.xmlpull.v1.XmlPullParser, android.view.animation.AnimationSet, android.util.AttributeSet),转化为Animation对象
private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
Animation anim = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("set")) {
anim = new AnimationSet(c, attrs);
createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
} else if (name.equals("alpha")) {
anim = new AlphaAnimation(c, attrs);
} else if (name.equals("scale")) {
anim = new ScaleAnimation(c, attrs);
} else if (name.equals("rotate")) {
anim = new RotateAnimation(c, attrs);
} else if (name.equals("translate")) {
anim = new TranslateAnimation(c, attrs);
} else {
throw new RuntimeException("Unknown animation name: " + parser.getName());
}
if (parent != null) {
parent.addAnimation(anim);
}
}
return anim;
}
官方定义:通过一段时间改变任何Object的属性形成动画 TODO 插值器(Interpolator)和估值器(TypeEvaluator)分析
java:
android.animation.ObjectAnimator
android.animation.Animator
android.animation.AnimatorSet
android.animation.ValueAnimator
TimeInterpolator
android.view.ViewPropertyAnimator 封装了ValueAnimator
Duration:一个动画的时长,默认300ms
Time interpolation: 一段代码块计算当前的属性的值
Frame refresh delay:刷新的时间,默认10ms
通过指定一系列类型(int, float, or color)的值,使这些类型的值动态变动
ObjectAnimator是ValueAnimator的子类,包含分时引擎和动画值计算,使目标对象的相应属性产生动画变动
- ObjectAnimator.ofInt(Object obj,String property,int ... intVals)返回ObjectAnimator对象 ObjectorAnimation主要是个工厂类,可以创建Int,float,object,argb类型的属性动画。
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
方法里面创建ObjectAnimator对象,
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
ObjectorAnimator构造方法里面,初始化属性的方法android.animation.ObjectAnimator#setTarget和android.animation.ObjectAnimator#setPropertyName,mTarget是个参数obj弱引用类型的属性(WeakReference),设置mTarget时候,如果和原先的obj不同则执行代码,暂停动画,重新初始化obj;setPropertyName方法,获取mValues数组的第一个值,该数组是个android.animation.PropertyValuesHolder类型数组,这个属性待会会详细说明,先看下setPropertyName将获取mValues数组第一个元素属性字符串,android.animation.ValueAnimator#mValuesMap移除属性字符串的key,设置成新的动画属性字符串,值是mValues的第一个元素。
- 接着ObjectAnimation.ofInt(Object obj,String property,int ... intVals) 执行完ObjectValue构造方法并调用其android.animation.ObjectAnimator#setIntValues(PropertyValuesHolder... values),初始化mValues和mValuesMap
@Override
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
2.1 intVals的数据封装为PropertyValuesHolder.ofInt(mProperty, values)的形式作为元素保存在属性mValues数组里面。mValues可以看出是保存一系列属性及其动画值的PropertyValuesHolder数组, PropertyValuesHolder的ofInt只是简单创建了IntPropertyValuesHolder对象的一行代码; IntPropertyValuesHolder继承了PropertyValuesHolder,构造方法调用父类的setIntValues,设置intVals以KeyframeSet.ofInt(int ... intVals)保存在android.animation.PropertyValuesHolder#mKeyframes中。
public void setValues(PropertyValuesHolder... values) {
int numValues = values.length;
mValues = values;
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
for (int i = 0; i < numValues; ++i) {
PropertyValuesHolder valuesHolder = values[i];
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
}
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
2.2 mValuesMap存储property字符串为key,以PropertyValuesHolder为值的数据,方便根据属性字符串,索引动画值。
3.android.animation.ValueAnimator#setInterpolator方法
设置android.animation.ValueAnimator#mInterpolator的属性,默认是android.view.animation.AccelerateDecelerateInterpolator,调用方法时,如果为null则设置为android.view.animation.LinearInterpolator。该属性是在android.animation.ValueAnimator#android.animation.PropertyValuesHolder#setAnimatedValue。
4. android.animation.ValueAnimator#setEvaluator方法 在Animator里只有ofApla方法和ofObject有用到,该方法根据设置mValues第一个元素PropertyValuesHolder的android.animation.PropertyValuesHolder#mEvaluator属性和步骤2.1的KeyframeSet的android.animation.KeyframeSet#mEvaluator属性。
- android.animation.ObjectAnimator#start方法
从android.animation.ValueAnimator#sAnimationHandler(java.lang.ThreadLocal)调用一个android.animation.ValueAnimator.AnimatorHandler(android.animation.ValueAnimator.AnimatorHandler)的类型的对象,保证每个线程有个对应的AnimationHandler,Animatiorndler用来循环动画的类。
如果没从ThreadLocal获取到AnimationHandler,则android.animation.ValueAnimator#start()。
如果有,则取消AnimatorHandler里面的所有Animator(android.animation.ValueAnimator.AnimationHandler#mAnimations,android.animation.ValueAnimator.AnimationHandler#mPendingAnimations,android.animation.ValueAnimator.AnimationHandler#mDelayedAnims)动画执行,调用Animator的cancel方法
接着说下android.animation.ValueAnimator#start(),方法里面只调用了方法,start(false)。这个方法初始化和启动android.animation.ValueAnimator.AnimationHandler start方法。
AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
if (prevPlayingState != SEEKED) {
setCurrentPlayTime(0);
}
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
如果马上执行的话,调用setCurrentPlayTime方法,更新属性。这个方法里面通过调用android.animation.ValueAnimator#setCurrentFraction获取动画因fraction,然后以参数的形式传入android.animation.ObjectAnimator#animateValue,反射更新动画。
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up.
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
在super.animateValue(fraction);时候获取值
//android.animation.ValueAnimator#animateValue
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
mValues[i].calculateValue(fraction);最总执行KeyFrameSet通过估值器获取值,参数fraction是通过插值器(时间分数=执行的时间/总时间)
//android.animation.KeyframeSet#getValue
public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
···
···
···
}
mValues[i].setAnimatedValue(target);这段代码通过反射,更新了对象的属性值。
接下来看android.animation.ValueAnimator.AnimationHandler#start方法。android.animation.ValueAnimator.AnimationHandler#mChoreographer调用android.animation.ValueAnimator.AnimationHandler#mAnimate,执行doAnimationFrame
void doAnimationFrame(long frameTime) {
mLastFrameTime = frameTime;
// mPendingAnimations holds any animations that have requested to be started
// We're going to clear mPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until mPendingAnimations
// is empty.
while (mPendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) mPendingAnimations.clone();
mPendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
if (anim.mStartDelay == 0) {
anim.startAnimation(this);
} else {
mDelayedAnims.add(anim);
}
}
}
android.animation.ValueAnimator.AnimationHandler#scheduleAnimation调用android.animation.ValueAnimator.AnimationHandler#mChoreographer#postCallback方法。android.animation.ValueAnimator.AnimationHandler#doAnimationFrame,通知界面android.animation.ValueAnimator#startAnimation
,执行android.animation.ValueAnimator#animationFrame(在这调用插值器),调用android.animation.ValueAnimator#animateValue更新target的数据。
-
该方法执行android.animation.ValueAnimator.AnimationHandler#mPendingAnimations的所有动画;
-
如果没有延迟执行startAnimation,否则加入的android.animation.ValueAnimator.AnimationHandler#mDelayedAnims里面。
// Next, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
int numDelayedAnims = mDelayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
ValueAnimator anim = mDelayedAnims.get(i);
if (anim.delayedAnimationFrame(frameTime)) {
mReadyAnims.add(anim);
}
}
int numReadyAnims = mReadyAnims.size();
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = mReadyAnims.get(i);
anim.startAnimation(this);
anim.mRunning = true;
mDelayedAnims.remove(anim);
}
mReadyAnims.clear();
}
- 接着遍历延迟动画列表,将可以准备好执行的动画加入mReadyAnims,并开始执行mReadyAnims的animstor,清空相关的延迟animator数据。
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = mAnimations.size();
for (int i = 0; i < numAnims; ++i) {
mTmpAnimations.add(mAnimations.get(i));
}
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
mTmpAnimations.clear();
if (mEndingAnims.size() > 0) {
for (int i = 0; i < mEndingAnims.size(); ++i) {
mEndingAnims.get(i).endAnimation(this);
}
mEndingAnims.clear();
}
- 遍历所有激活的动画,判断是否结束(android.animation.ValueAnimator#doAnimationFrame方法判断是否结束动画),并把结束的animator做处理,处理为于android.animation.ValueAnimator#endAnimation方法,并将改animator移除AnimatorHandler,发送通知android.animation.Animator.AnimatorListener#onAnimationEnd。通过android.animation.ValueAnimator#animationFrame更新界面。
// Schedule final commit for the frame.
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
- 调用android.animation.ValueAnimator.AnimationHandler#commitAnimationFrame,调用激活的animator的commitAnimationFrame方法
// If there are still active or delayed animations, schedule a future call to
// onAnimate to process the next frame of the animations.
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
- 如果有animator未执行,则继续调用scheduleAnimation方法。
#####取消ObjectAnimator动画 android.animation.ValueAnimator#cancel,如果没有启动的animator,先执行android.animation.Animator.AnimatorListener#onAnimationStart方法,然后发送通知给监听对象android.animation.Animator.AnimatorListener#onAnimationCancel,最后android.animation.ValueAnimator#endAnimation,清除当前AnimatorHandler的所有动画信息。 ###android.view.Choreographer
- svg code PathInfo.java
- 吸入动画
Mesh表示网格,说得通俗一点,可以将画板想像成一张格子布,在这个张布上绘制图片。Android 吸入动画效果详解 构建Mesh Path Canvas.drawBitmapMesh
canvas.drawBitmapMesh(mBitmap,
mInhaleMesh.getWidth(),
mInhaleMesh.getHeight(),
mInhaleMesh.getVertices(),
0, null, 0, mPaint);
Animation
|____src
| |____main
| | |____AndroidManifest.xml
| | |____java
| | | |____edu
| | | | |____ptu
| | | | | |____androidanimation
| | | | | | |____AnimationActivity.java
| | | | | | |____animation
| | | | | | | |____AnimationFactory.java #抽象工厂实现,可以返回ViewAnimation,AnimationDrawable,Animator动画类
| | | | | | | |____CustomAnimation.java
| | | | | | |____graphics
| | | | | | | |____PathUtils.java
| | | | | | |____viewpager
| | | | | | | |____ColorFragment.java
| | | | | | | |____impl
| | | | | | | | |____CustomTransformer.java
| | | | | | | |____ViewPagerFactory.java
CustomView
|____src
| |____main
| | |____AndroidManifest.xml
| | |____java
| | | |____edu
| | | | |____ptu
| | | | | |____customview
| | | | | | |____CustomAbstractView.java
| | | | | | |____CustomCompositeView.java
| | | | | | |____MainActivity.java
| | | | | | |____CustomDrawView.java
| | | | | | |____element
| | | | | | | |____impl
| | | | | | | | |____LineChart.java
| | | | | | | | |____PathInfo.java #绘图元素实现类,路径绘制( element implement)
| | | | | | | |____IDrawElement.java #绘图元素接口(drawable element interface)