策略模式的定义:
策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
策略模式的使用场景:
1.针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
2.需要安全封装多种同一类型的操作时。
3.出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体的子类时。
策略模式UML图
Android源码中的策略模式的实现
时间插值器(TimeInterpolator)
时间插值器的作用:根据时间流逝的百分比来计算当前属性值改变的百分比。系统预置的有:线性插值器(LinearInterpolator),用于匀速动画;加速减速插值器(AccelerateDecelerateInterpolator)用于起始时动画加速,结尾时动画减速;减速插值器(DecelerateInterpolator)用于随着时间的推移动画越来越慢,即减速动画。这些插值器是策略模型的典型应用。
(类型估值器)TypeEvaluator
它的作用是根据当前属性改变的百分比来计算改变后的属性值,也就说TypeEvaluator计算得到的才是属性的值。
时间插值器计算得到当前时间点的时间流逝百分比,TypeEvaluator根据这个百分比、属性起始值、目标值来计算出当前时刻该属性的值,最后这个值被设置给View,不断重复这个过程就形成了动画。系统预置的有整型属性(IntEvaluator)、浮点型属性(FloatEvaluator)、Color属性(ArgbEvaluato)。
/**
* Start the specified animation now.
*
* @param animation the animation to start now
*/
public void startAnimation(Animation animation) {
//1.初始化动画开始时间
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
//2.对View设置动画
setAnimation(animation);
//3.刷新父类缓存
invalidateParentCaches();
//4.刷新View本身以及子View
invalidate(true);
}
startAnimation中首先设置了动画的起始时间,然后将该动画设置到该View中,最后再向ViewGroup请求刷新视图,随后ViewGroup就会调用dispatchDraw方法对这个View所在的区域进行重绘。对于某一个View的重绘最终调用ViewGroup中的drawChild(Canvas canvas,View child,long drawingtime)方法。
#View
/**
* This is where the invalidate() work actually happens. A full invalidate()
* causes the drawing cache to be invalidated, but this function can be
* called with invalidateCache set to false to skip that invalidation step
* for cases that do not need it (for example, a component that remains at
* the same dimensions with the same content).
*
* @param invalidateCache Whether the drawing cache for this view should be
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View's contents or
* dimensions have not changed.
* @hide
*/
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
#View
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
... ...
}
#ViewGroup
@Override
protected void dispatchDraw(Canvas canvas) {
.....
drawChild(canvas, child, drawingTime)
}
#ViewGroup
/**
* Draw one child of this View Group. This method is responsible for getting
* the canvas in the right state. This includes clipping, translating so
* that the child's scrolled origin is at 0, 0, and applying any animation
* transformations.
*
* @param canvas The canvas on which to draw the child
* @param child Who to draw
* @param drawingTime The time at which draw is occurring
* @return True if an invalidate() was issued
*/
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
drawChild方法只是进行了一个转发,所以要看下View的draw(Canvas canvas, ViewGroup parent, long drawingTime)是如何调用Animation的。
#View
/**
* This method is called by ViewGroup.drawChild() to have each child view draw itself.
*
* This is where the View specializes rendering behavior based on layer type,
* and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
... ....
//查看是否需要清除动画信息
final int parentFlags = parent.mGroupFlags;
//... ...
//获取设置的动画信息
final Animation a = getAnimation();
if (a != null) {
//绘制动画
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
} else {
if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) {
// No longer animating: clear out old animation matrix
mRenderNode.setAnimationMatrix(null);
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
if (!drawingWithRenderNode
&& (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
final Transformation t = parent.getChildTransformation();
final boolean hasTransform = parent.getChildStaticTransformation(this, t);
if (hasTransform) {
final int transformType = t.getTransformationType();
transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
}
}
}
}
可以看出在父类调用VIew的draw方法中,会先判断是否设置了清除动画的标记,然后再获取该View动画的信息,如果设置了动画,就会调用View中的applyLegacyAnimation 方法。
#View
/**
* Utility function, called by draw(canvas, parent, drawingTime) to handle the less common
* case of an active Animation being run on the view.
*/
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
Transformation invalidationTransform;
final int flags = parent.mGroupFlags;
//1. 判断动画是否已经初始化过。
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();
}
//获取Transformation 对象,存储动画信息
final Transformation t = parent.getChildTransformation();
//2.调用了Animation的getTransformation方法,这里就是通过计算获取动画的相关值。
boolean more = a.getTransformation(drawingTime, t, 1f);
... ...
if (more) {
//3.根据具体实现,判断当前动画类型是否需要进行调整位置大小,然后刷新不同区域。
if (!a.willChangeBounds()) {
... ...
} else {
if (parent.mInvalidateRegion == null) {
parent.mInvalidateRegion = new RectF();
}
final RectF region = parent.mInvalidateRegion;
//获取重绘的区域
a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
invalidationTransform);
// The child need to draw an animation, potentially offscreen, so
// make sure we do not cancel invalidate requests
parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
// 重新计算有效区域
final int left = mLeft + (int) region.left;
final int top = mTop + (int) region.top;
//更新这块区域
parent.invalidate(left, top, left + (int) (region.width() + .5f),
top + (int) (region.height() + .5f));
}
}
return more;
}
在applyLegacyAnimation 中主要的操作是动画的初始化、动画操作、界面刷新。在applyLegacyAnimation 中首先判断动画是否进行了初始化,如果未初始化则先初始化,然后调用动画监听器的onStart()函数。 动画的具体实现是通过Animation中的a.getTransformation(drawingTime, t, 1f) 方法。
#Animation
/**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
* otherwise.
*
* @param currentTime Where we are in the animation. This is wall clock time.
* @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @param scale Scaling factor to apply to any inputs to the transform operation, such
* pivot points being rotated or scaled around.
* @return True if the animation is still running
*/
public boolean getTransformation(long currentTime, Transformation outTransformation,
float scale) {
mScaleFactor = scale;
return getTransformation(currentTime, outTransformation);
}
上面的方法中,获取缩放系数和调用getTransformation(currentTime, outTransformation)来计算和应用动画效果。
#Animation
/**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
* otherwise.
*
* @param currentTime Where we are in the animation. This is wall clock time.
* @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @return True if the animation is still running
*/
/**
* The interpolator used by the animation to smooth the movement.
*/
Interpolator mInterpolator;
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {
mStartTime = currentTime;
}
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
//1.计算当前时间的流逝百分比
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
// time is a step-change with a zero duration
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;
}
//2.通过插值器获取动画执行百分比
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
//3.应用动画效果
applyTransformation(interpolatedTime, outTransformation);
}
//4.如果动画执行完毕,那么触发动画完成的回调或者执行重复动画等操作
if (expired) {
if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
//触发动画完成的回调
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
//执行重复动画的回调
fireAnimationRepeat();
}
}
if (!mMore && mOneMoreTime) {
mOneMoreTime = false;
return true;
}
return mMore;
}
在上述函数中,首先会获取已经流逝的动画执行时间百分比,然后通过插值器来重新计算这个百分比,也就是调用了插值器的getInterpolation(float input) 方法来获取当前的时间百分比,并且以此来计算当前动画的属性值,例如,线性插值器的输出百分比就是输入百分比,不做任何处理,使得动画的速率不会发生变化。
#LinearInterpolator 线性插值器
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
//线性插值器的输出百分比就是输入百分比,不做任何处理,使得动画的速率不会发生变化。
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
#AccelerateInterpolator (加速插值器)
@HasNativeInterpolator
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
}
/**
* Constructor
*
* @param factor Degree to which the animation should be eased. Seting
* factor to 1.0f produces a y=x^2 parabola. Increasing factor above
* 1.0f exaggerates the ease-in effect (i.e., it starts even
* slower and ends evens faster)
*/
public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}
public AccelerateInterpolator(Context context, AttributeSet attrs) {
this(context.getResources(), context.getTheme(), attrs);
}
/** @hide */
public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
TypedArray a;
if (theme != null) {
a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0);
} else {
a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator);
}
mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f);
mDoubleFactor = 2 * mFactor;
setChangingConfiguration(a.getChangingConfigurations());
a.recycle();
}
public float getInterpolation(float input) {
//默认为1.0f,随着时间的推移,变化范围大
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
}
默认情况下,AccelerateInterpolator的getInterpolation方法中会对input进行乘方操作,这个input就是流逝的时间百分比,input的取值为0.0f~1.0f,当input逐渐增大时,input*input的变化范围越来越大。使得动画的属性值在同一时间段内的变化范围更大,从而实现了加速动画的效果。
#Animation(Animation类中的applyTransformation方法为空)
/**
* Helper for getTransformation. Subclasses should implement this to apply
* their transforms given an interpolation value. Implementations of this
* method should always replace the specified Transformation or document
* they are doing otherwise.
*
* @param interpolatedTime The value of the normalized time (0.0 to 1.0)
* after it has been run through the interpolation function.
* @param t The Transformation object to fill in with the current
* transforms.
*/
protected void applyTransformation(float interpolatedTime, Transformation t) {
}
在调用了插值器的getInterpolation方法之后,会继续调用动画类的applyTransformation(interpolatedTime, outTransformation);方法将属性应用到对应的对象中。applyTransformation在Animation基类中是空实现,那么我们选择缩放动画(ScaleAnimation)来看看具体实现。
public class ScaleAnimation extends Animation
#ScaleAnimation
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float sx = 1.0f;
float sy = 1.0f;
float scale = getScaleFactor();
if (mFromX != 1.0f || mToX != 1.0f) {
sx = mFromX + ((mToX - mFromX) * interpolatedTime);
}
if (mFromY != 1.0f || mToY != 1.0f) {
sy = mFromY + ((mToY - mFromY) * interpolatedTime);
}
//通过Matrix实现View的缩放
if (mPivotX == 0 && mPivotY == 0) {
t.getMatrix().setScale(sx, sy);
} else {
t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
}
}
当执行完applTransformation之后,View的属性就发生了变化,不断地重复这个过程,动画就产生了。在这个过程中,插值器扮演了很重要的角色。它将动画的速率计算 封装到一个抽象中,也就是一个Interpolator中,该接口只有一个getInterpolation(float input)方法,通过这个方法来修改动画的流逝时间百分比,以此达到动画的加速、减速等效果。Interpolator就是这个计算策略的抽象,LinearInterpolator、CycleInterpolator等插值器就是具体的实现策略,通过不同的插值器实现不同的动态效果。
Animation中对mInterpolator 设置与获取。
* The interpolator used by the animation to smooth the movement.
*/
Interpolator mInterpolator;
#Animation
/**
* Sets the acceleration curve for this animation. Defaults to a linear
* interpolation.
*
* @param i The interpolator which defines the acceleration curve
* @attr ref android.R.styleable#Animation_interpolator
*/
public void setInterpolator(Interpolator i) {
mInterpolator = i;
}
#Animation
/**
* Gets the acceleration curve type for this animation.
*
* @return the {@link Interpolator} associated to this animation
* @attr ref android.R.styleable#Animation_interpolator
*/
public Interpolator getInterpolator() {
return mInterpolator;
}
#Animation
/**
* Gurantees that this animation has an interpolator. Will use
* a AccelerateDecelerateInterpolator is nothing else was specified.
*/
protected void ensureInterpolator() {
if (mInterpolator == null) {
mInterpolator = new AccelerateDecelerateInterpolator();
}
}
参考《Android源码设计模式》