Android设计模式之策略模式

策略模式的定义:

策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

策略模式的使用场景:

1.针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。

2.需要安全封装多种同一类型的操作时。

3.出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体的子类时。

策略模式UML图

 

Android源码中的策略模式的实现

时间插值器(TimeInterpolator)

时间插值器的作用:根据时间流逝的百分比来计算当前属性值改变的百分比。系统预置的有:线性插值器(LinearInterpolator),用于匀速动画;加速减速插值器(AccelerateDecelerateInterpolator)用于起始时动画加速,结尾时动画减速;减速插值器(DecelerateInterpolator)用于随着时间的推移动画越来越慢,即减速动画。这些插值器是策略模型的典型应用。

(类型估值器)TypeEvaluator

它的作用是根据当前属性改变的百分比来计算改变后的属性值,也就说TypeEvaluator计算得到的才是属性的值。

扫描二维码关注公众号,回复: 4252017 查看本文章

时间插值器计算得到当前时间点的时间流逝百分比,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源码设计模式》

 

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/84196178