fdhbwwwbb0001com19908836661用来操作策略的上下文环境

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。Android中最典型的的策略设计模式是动画插值器的使用,具体怎么使用的,将是本文所重点所写的内容。

1、UML类图

image

Context:用来操作策略的上下文环境。
Strategy : 策略的抽象。
ConcreteStrategyA、ConcreteStrategyB : 具体的策略实现。
2、Android源码中的模式实现

日常的Android开发中经常会用到动画,Android中最简单的动画就是Tween Animation了,当然帧动画和属性动画也挺方便的,但是基本原理都类似,毕竟动画的本质都是一帧一帧的展现给用户的,只不要当fps小于60的时候,人眼基本看不出间隔,也就成了所谓的流畅动画。(注:属性动画是3.0以后才有的,低版本可采用NineOldAndroids来兼容。而动画的动态效果往往也取决于插值器Interpolator不同,我们只需要对Animation对象设置不同的Interpolator就可以实现不同的效果,这是怎么实现的呢?

首先要想知道动画的执行流程,还是得从View入手,因为Android中主要针对的操作对象还是View,所以我们首先到View中查找,我们找到了View.startAnimation(Animation animation)这个方法。

public void startAnimation(Animation animation) {
//初始化动画开始时间
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
//对View设置动画
setAnimation(animation);
//刷新父类缓存
invalidateParentCaches();
//刷新View本身及子View
invalidate(true);
}
考虑到View一般不会单独存在,都是存在于某个ViewGroup中,所以google使用动画绘制的地方选择了在ViewGroup中的drawChild(Canvas canvas, View child, long drawingTime)方法中进行调用子View的绘制。

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
再看下View中的draw(Canvas canvas, ViewGroup parent, long drawingTime)方法中是如何调用使用Animation的

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
//...

//查看是否需要清除动画信息
final int flags = parent.mGroupFlags;
if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) {
    parent.getChildTransformation().clear();
    parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}

//获取设置的动画信息
final Animation a = getAnimation();
if (a != null) {
    //绘制动画
    more = drawAnimation(parent, drawingTime, a, scalingRequired);
    concatMatrix = a.willChangeTransformationMatrix();
    if (concatMatrix) {
        mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
    }
    transformToApply = parent.getChildTransformation();
} else {
    //...
}

}
可以看出在父类调用View的draw方法中,会先判断是否设置了清除到需要做该表的标记,然后再获取设置的动画的信息,如果设置了动画,就会调用View中的drawAnimation方法,具体如下:

private boolean drawAnimation(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();//动画开始监听
}

//获取Transformation对象,存储动画的信息
final Transformation t = parent.getChildTransformation();
//调用了Animation的getTransformation()方法,这里就是通过计算获取动画的相关值
boolean more = a.getTransformation(drawingTime, t, 1f);

//代码省略。。。

if (more) {
    //根据具体实现,判断当前动画类型是否需要进行调整位置大小,然后刷新不同的区域
    if (!a.willChangeBounds()) {
        //...

    }else{
        //...重新重绘的区域、重新计算有效区域、更新这块区域
    }
}
return more;

}
其中主要的操作是动画始化、动画操作、界面刷新。动画的具体实现是调用了Animation中的getTransformation(long currentTime, Transformation outTransformation,float scale)方法。

public boolean getTransformation(long currentTime, Transformation outTransformation,
float scale) {
mScaleFactor = scale;
return getTransformation(currentTime, outTransformation);
}
在上面的方法中主要是获取缩放系数和调用Animation.getTransformation(long currentTime, Transformation outTransformation)来计算和应用动画效果。

public boolean getTransformation(long currentTime, Transformation outTransformation) {
//代码省略。。。
float normalizedTime;
if (duration != 0) {
//1、计算当前的时间的流逝百分比
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 ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
    //代码省略。。。

    //2、通过插值器获取动画的执行百分比
    final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
    //3、应用动画效果
    applyTransformation(interpolatedTime, outTransformation);
}

//4、如果动画指定完毕,那么触发动画完成的回调或者指定重复动画等操作

if (!mMore && mOneMoreTime) {
    mOneMoreTime = false;
    return true;
}

return mMore;

}
其中最重要的代码通过红笔标注出来了。

很容易发现Android系统中在处理动画的时候会调用插值器中的getInterpolation(float input)方法来获取当前的时间点,依次来计算当前变化的情况。这就不得不说到Android中的插值器Interpolator,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。看到这里,希望你已经理解了插值器和估值器的区别。之前有一个总结:插值器会传入一个参数,这个参数就是一个时间进度值,也就是所谓的当前时间的流逝百分比。它相当于时间的概念,通过setDuration()制定了动画的时长,在这个时间范围内,动画进度是一点点增加的,相当于一首歌,它的进度从0到1意思一样。插值器需要根据这个值计算返回动画的数值进度,这个值可以根据插值器的不同来设置不同的算法,最终这个值可以在监听回调中拿到。

系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等,如图:

image

由于初期比较旧的版本采用的插值器是TimeInterpolator抽象,google采用了多加一层接口继承来实现兼容也不足为怪了。很显然策略模式在这里作了很好的实现,Interpolator就是处理动画时间的抽象,LinearInterpolator、CycleInterpolator等插值器就是具体的实现策略。插值器与Animation的关系图如下:

image

这里以LinearInterpolator、AccelerateInterpolator和CycleInterpolator为例:

LinearInterpolator:

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public float getInterpolation(float input) {
return input;
}
}
CycleInterpolator:

public float getInterpolation(float input) {
return (float)(Math.sin(2 mCycles Math.PI input));
}
可以看出LinearInterpolator中计算当前时间的方法是做线性运算,也就是返回input
1,所以动画会成直线匀速播放出来,而CycleInterpolator是按照正弦运算,所以动画会正反方向跑一次,其它插值器依次类推。不同的插值器的计算方法都有所差别,用户设置插值器以实现动画速率的算法替换。

我们再来看看加速插值器的代码:

public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}

public AccelerateInterpolator(Context context, AttributeSet attrs) {
this(context.getResources(), context.getTheme(), attrs);
}

public float getInterpolation(float input) {
if (mFactor == 1.0f) {//默认1.0f,锁着事件的推移,变化范围增大
return input input;
} else {//用户自己设置了值,根据这个值与1.0最大值乘积返回回去
return (float)Math.pow(input, mDoubleFactor);
}
}
我们看到,默认情况下,AccelerateInterpolator 的getInterpolation 方法中会对input进行乘方运算,这个input就是流逝时间百分比(时间进度值)。input取值范围为0.0f~1.0f,当input逐渐增大时,input
input的变化范围越来越大,使得动画属性值在同一时间段内的变化范围更大,从而实现了加速动画的效果。例如,动画指定时间为1000ms,使用的是AccelerateInterpolator 插值器,在动画指定了100ms时百分比为0.1,此时通过插值器计算得到的动画数值进度值为0.01,;又经过100ms,此时的百分比为0.2,经过插值器计算变为0.04,;执行到0..ms,计算得到0.09....可以看到,在相同的100ms内百分比变化频率逐渐增大。100~200ms变化值0.03,200~300ms变化值为0.05,这样在同一个时间段内百分比差距越来越大,也就形成了加速的效果。

在调用了插值器的getInterpolation 方法后,会继续调用动画类的applyTransformation方法将属性应用到对应的对象中。在Animation中是空实现,这里选择TranslateAnimation来看看它的具体实现:

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float dx = mFromXDelta;
float dy = mFromYDelta;
if (mFromXDelta != mToXDelta) {
dx = mFromXDelta + ((mToXDelta - mFromXDelta) interpolatedTime);
}
if (mFromYDelta != mToYDelta) {
dy = mFromYDelta + ((mToYDelta - mFromYDelta)
interpolatedTime);
}
t.getMatrix().setTranslate(dx, dy);
}
当执行完applyTransformation后,View就发生了改变,不断重复这个过程,动画就随之产生了。注意这里是Matrix类的setTranslate方法,并没有真正的修改View的属性,这里也是View动画跟属性动画本质区别的地方了(个人这么理解的,如果有误还望指正)

原文地址:http://blog.51cto.com/14182580/2344736

时间: 2024-08-30 09:34:45

fdhbwwwbb0001com19908836661用来操作策略的上下文环境的相关文章

javascript执行上下文环境

今天,想对javascript的执行上下文环境做一个深入的理解.之前一直都有这打算的,但无奈忙着忙着就忘记了.今天看了一篇博客,对执行上下文环境的理解可以说是醍醐灌顶. 一.对一段代码的理解开始 在浏览器的控制台输入以下代码段: 这个结果说明,代码在一句句执行之前,浏览器已经做了一些准备工作,所以,后面两个都没有报错.下面再看一段代码: 这个结果说明,函数声明时,把整个函数赋值了,而函数表达式和变量一样,只是申明. 我们总结一下,在"准备工作"中完成了哪些工作: 变量.函数表达式--变

深入理解javascript原型和闭包(13)-【作用域】和【上下文环境】

上文简单介绍了作用域,本文把作用域和上下文环境结合起来说一下,会理解的更深一些. 如上图,我们在上文中已经介绍了,除了全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了.而不是在函数调用时确定. 下面我们将按照程序执行的顺序,一步一步把各个上下文环境加上.另外,对上下文环境不了解的朋友,可以去看看之前的两篇文章: http://www.cnblogs.com/wangfupeng1988/p/3986420.html http://www.cnblogs.com/wan

深入理解javascript原型和闭包(17)——补充:上下文环境和作用域的关系

摘自:http://www.cnblogs.com/wangfupeng1988/p/4000798.html:作者:王福朋: 本系列用了大量的篇幅讲解了上下文环境和作用域,有些人反映这两个是一回儿事.本文就用一个小例子来说明一下,作用域和上下文环境绝对不是一回事儿. 再说明之前,咱们先用简单的语言来概括一下这两个的区别. 00 上下文环境: 可以理解为一个看不见摸不着的对象(有若干个属性),虽然看不见摸不着,但确实实实在在存在的,因为所有的变量都在里面存储着,要不然咱们定义的变量在哪里存? 另

深入理解javascript原型和闭包(18)——补充:上下文环境和作用域的关系

本系列用了大量的篇幅讲解了上下文环境和作用域,有些人反映这两个是一回儿事.本文就用一个小例子来说明一下,作用域和上下文环境绝对不是一回事儿. 再说明之前,咱们先用简单的语言来概括一下这两个的区别. 00 上下文环境: 可以理解为一个看不见摸不着的对象(有若干个属性),虽然看不见摸不着,但确实实实在在存在的,因为所有的变量都在里面存储着,要不然咱们定义的变量在哪里存? 另外,对于函数来说,上下文环境是在调用时创建的,这个很好理解.拿参数做例子,你不调用函数,我哪儿知道你要给我传什么参数? 01 作

执行上下文环境—JavaScript面向对象高级

#  执行上下文环境 # **定义**:执行函数的时候,会产生一个上下文的对象,里面保存变量,函数声明和this. **作用**:用来保存本次运行时所需要的数据 在产生执行上下文(执行上下文环境)时,浏览器会做以下三个准备工作: 1.提取var 声明的变量,并赋值为 undefined 2.提取声明式函数. 3.给this赋值,指向window或当前对象. 函数每执行一次都会产生一个执行上下文, 因为函数参数不同会有不同的计算结果, ## 全局上下文环境 ## 全局级别的代码,如多个script

JavaScript作用域、上下文环境、函数对象的定义与调用、匿名函数的定义与调用、闭包

提到闭包总给人很高深的感觉,网上的例子也数不胜数.但是我发现相当一部分并不容易理解.根据我的观察,是因为这些例子把标题中提到的概念糅杂在了一起,往往越看越糊涂.所以我希望化整为零,拆成简单例子来解释. 1.先看作用域: JavaScript作用域只有两种--全局作用域和函数内作用域,没有代码块作用域.示例: function loop(){ for(var i=0;i<5;i++){ //doSomething; } alert(i); } loop(); //执行函数结果为5. 尽管变量i已经

上下文环境

##执行上下文环境 在一段JS真正运行之前浏览器已经做了一些准备工作:     1.变量:变量的申明,默认值为undefined:     2.this:赋值:     3.函数申明:赋值 变量: console.log(a)  ---> a is not undefined console.log(a)-----> undefined     var a console.log(a)-----> undefined     var a = 10 this: console.log(th

执行上下文环境

学习文章------①王福明的博客   ②汤姆大叔的博客 总结笔记 知识点: 1.执行上下文环境,简单地说就是在执行代码之前把所有变量拿出来赋值. 2.处于执行上下文栈最上面的为活动状态 全局上下文环境 准备工作 ①变量的声明(普通变量,函数表达式) ②函数声明 ③this的指向(例如:console.log(this)) 函数体上下文环境 重点:Ⅰ.函数是在定义的时候确定了自由变量的作用域,而不是在调用的时候 Ⅱ.函数每调用一次,就会产生一个上下文环境        Ⅲ.函数执行完之后,上下文

Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8201936 在前文中,我们简要介绍了Android应用程序窗口的框架.Android应用程序窗口在运行的过程中,需要访问一些特定的资源或者类.这些特定的资源或者类构成了Android应用程序的运行上下文环境,Android应用程序窗口可以通过一个Context接口来访问它,这个Context接口也是我们在开发应用程序时经常碰到的.在本文中,我们