一、模板方法模式定义

模板方法模式定义:
defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
定义一个方法操作算法的框架(骨架结构),而将一些步骤延迟到子类中。模板方法使子类可以在不改变一个算法的结构的情况下,就可以重定义该算法的某些特定步骤。

  如上图所示(截取自《Head First Design Patterns》一书),具体各个部分有什么作用见上图,这里就不啰嗦了。

二、模板方法模式优势

封装不变部分,扩展可变部分。把不变部分的算法封装到父类实现,而可变部分的根据子类的具体需要,则可以通过继承来扩展。

提取公共部分,构成一个“模板”,模板的作用在于对算法或者流程的一个结构化、规范化,子类不能修改“模板方法”的整个算法骨架或者流程的顺序等,只能根据自身的不同,对模板方法中算法的某一步进行扩展。

行为由父类控制,子类实现。子类可以通过扩展的方法增加相应的功能,符合开闭原则。

三、模板方法模式在Android源码中的应用

  在Android源码中,View中的Draw()方法就是一个“模板方法”。它定义了一系列“Draw”过程,主要包括这几个步骤(截取自源代码):

/*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      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)
         */

具体函数代码如下所示:

  1.  

其中第三步( Step 3)Draw view's content函数:

  1. /**
  2. * Implement this to do your drawing.
  3. *
  4. * @param canvas the canvas on which the background will be drawn
  5. */
  6. protected void onDraw(Canvas canvas) {
  7. }

第四步( Step 4) draw children

  1. /**
  2. * Called by draw to draw the child views. This may be overridden
  3. * by derived classes to gain control just before its children are drawn
  4. * (but after its own view has been drawn).
  5. * @param canvas the canvas on which to draw the view
  6. */
  7. protected void dispatchDraw(Canvas canvas) {
  8. }

  从上面的Draw()“模板方法”可以看出,当继承View子类中,如果要重写或者扩展这个方法时,整个方法流程和基本内容不能够修改,子类只能通过扩展onDraw(Canvas canvas)和dispatchDraw(Canvas canvas)两个函数,使子类自己的View显示效果和别的具体子类的不同。现在来看看继承自View类的具体子类如何扩展Draw()模板方法显示自己的与众不同:

  1、TextView类中重写了OnDraw函数

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. if (mPreDrawState == PREDRAW_DONE) {
  4. final ViewTreeObserver observer = getViewTreeObserver();
  5. observer.removeOnPreDrawListener(this);
  6. mPreDrawState = PREDRAW_NOT_REGISTERED;
  7. }
  8.  
  9. if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
  10.  
  11. restartMarqueeIfNeeded();
  12.  
  13. // Draw the background for this view
  14. super.onDraw(canvas);
  15.  
  16. final int compoundPaddingLeft = getCompoundPaddingLeft();
  17. final int compoundPaddingTop = getCompoundPaddingTop();
  18. final int compoundPaddingRight = getCompoundPaddingRight();
  19. final int compoundPaddingBottom = getCompoundPaddingBottom();
  20. final int scrollX = mScrollX;
  21. final int scrollY = mScrollY;
  22. final int right = mRight;
  23. final int left = mLeft;
  24. final int bottom = mBottom;
  25. final int top = mTop;
  26.  
  27. final Drawables dr = mDrawables;
  28. if (dr != null) {
  29. /*
  30. * Compound, not extended, because the icon is not clipped
  31. * if the text height is smaller.
  32. 。。。
  33. }

  2、SurfaceView重写了dispatchDraw()函数:

  1. @Override
  2. protected void dispatchDraw(Canvas canvas) {
  3. if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
  4. // if SKIP_DRAW is cleared, draw() has already punched a hole
  5. if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
  6. // punch a whole in the view-hierarchy below us
  7. canvas.drawColor(0, PorterDuff.Mode.CLEAR);
  8. }
  9. }
  10. super.dispatchDraw(canvas);
  11. }

  3、ViewGroup类重写了dispatchDraw()函数:

  1. @Override
  2. protected void dispatchDraw(Canvas canvas) {
  3. final int count = mChildrenCount;
  4. final View[] children = mChildren;
  5. int flags = mGroupFlags;
  6.  
  7. if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
  8. final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
  9.  
  10. final boolean buildCache = !isHardwareAccelerated();
  11. for (int i = 0; i < count; i++) {
  12. final View child = children[i];
  13. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
  14. final LayoutParams params = child.getLayoutParams();
  15. attachLayoutAnimationParameters(child, params, i, count);
  16. bindLayoutAnimation(child);
  17. if (cache) {
  18. child.setDrawingCacheEnabled(true);
  19. if (buildCache) {
  20. child.buildDrawingCache(true);
  21. }
  22. }
  23. }
  24. }
  25.  
  26. final LayoutAnimationController controller = mLayoutAnimationController;
  27. if (controller.willOverlap()) {
  28. mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
  29. }
  30.  
  31. controller.start();
  32.  
  33. mGroupFlags &= ~FLAG_RUN_ANIMATION;
  34. mGroupFlags &= ~FLAG_ANIMATION_DONE;
  35.  
  36. if (cache) {
  37. mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
  38. }
  39.  
  40. if (mAnimationListener != null) {
  41. mAnimationListener.onAnimationStart(controller.getAnimation());
  42. }
  43. }
  44.  
  45. int saveCount = 0;
  46. final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
  47. if (clipToPadding) {
  48. saveCount = canvas.save();
  49. canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
  50. mScrollX + mRight - mLeft - mPaddingRight,
  51. mScrollY + mBottom - mTop - mPaddingBottom);
  52.  
  53. }
  54.  
  55. // We will draw our child's animation, let's reset the flag
  56. mPrivateFlags &= ~DRAW_ANIMATION;
  57. mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
  58.  
  59. boolean more = false;
  60. final long drawingTime = getDrawingTime();
  61.  
  62. if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
  63. for (int i = 0; i < count; i++) {
  64. final View child = children[i];
  65. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
  66. more |= drawChild(canvas, child, drawingTime);
  67. }
  68. }
  69. } else {
  70. for (int i = 0; i < count; i++) {
  71. final View child = children[getChildDrawingOrder(count, i)];
  72. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
  73. more |= drawChild(canvas, child, drawingTime);}}}// Draw any disappearing views that have animationsif(mDisappearingChildren !=null){finalArrayList<View> disappearingChildren = mDisappearingChildren;finalint disappearingCount = disappearingChildren.size()-1;// Go backwards -- we may delete as animations finishfor(int i = disappearingCount; i >=0; i--){finalView child = disappearingChildren.get(i);
  74. more |= drawChild(canvas, child, drawingTime);}}if(clipToPadding){
  75. canvas.restoreToCount(saveCount);}// mGroupFlags might have been updated by drawChild()
  76. flags = mGroupFlags;if((flags & FLAG_INVALIDATE_REQUIRED)== FLAG_INVALIDATE_REQUIRED){
  77. invalidate(true);}if((flags & FLAG_ANIMATION_DONE)==0&&(flags & FLAG_NOTIFY_ANIMATION_LISTENER)==0&&
  78. mLayoutAnimationController.isDone()&&!more){// We want to erase the drawing cache and notify the listener after the// next frame is drawn because one extra invalidate() is caused by// drawChild() after the animation is over
  79. mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;finalRunnableend=newRunnable(){publicvoid run(){
  80. notifyAnimationListener();}};
  81. post(end);}}

  总之,首先在View.Draw()“模板方法”函数将设计界面分成6个步骤,合并不变部分,然后将可变部分提取、独立出来,让子类TextView等扩展修改成自己“独特”的界面效果。在我们自己的实际开发中,如果要设计自己的界面效果大部分的时候,我们都是覆盖重写onDraw(Canvas canvas)函数。

  此外:根据模版方法中的方法,可以分为两大类:模版方法(Template Method)和基本方法(Primitive Method)。其中我们这里的例子Draw()函数就是一个“模板方法”。

而基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method):

抽象方法:一个抽象方法由抽象类声明,由具体子类实现。

具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。

钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。我们这里的onDraw()函数就是一个钩子方法。

  最后记住:

  本人能力和时间有限(缺少“模式使用”内容,以后会添加),写的很粗糙,恭候大家的批评指正,谢谢~~~

Android源码学习之模板方法模式应用的更多相关文章

  1. 《Android源码设计模式》--模板方法模式

    No1: 模板方法模式包括:抽象类(其中定义了一系列顺序方法).具体实现类A.具体实现类B 如果子类有实现不一样的细节,重写父类的某个方法即可 No2: AsyncTask对象调用execute方法后 ...

  2. Android源码学习之装饰模式应用

    首先得了解最基础的装饰器模式 参考 设计模式之八 --- 装饰模式(Decorator) 参考链接:http://blog.csdn.net/cjjky/article/details/7478788 ...

  3. Android源码-学习随笔

    在线代码网站1:http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/ 书籍: ...

  4. 《Android源码设计模式》--Builder模式

    No1: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 No2: 在Android源码中,最常用到的Builder模式就是AlertDialog.Builder No3: ...

  5. Android源码学习(一) 数据集观察者

    查看Android源码发现这个,决定记下下来. 1.在android.database这个包下面,存在这样一个抽象类DataSetObserver,里面包括onChanged()和onInvalida ...

  6. Android 源码学习

    工具篇:如何使用 Visual Studio Code 阅读 Android 源码:https://jekton.github.io/2018/05/11/how-to-read-android-so ...

  7. 结合JDK源码看设计模式——模板方法模式

    前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...

  8. 【转】Android源码学习(2)使用Git和Repo进行版本管理

    原文网址:http://blog.chinaunix.net/uid-26074270-id-2458828.html Android项目采用Git和Repo进行版本管理.在大多数情况下,Git都可以 ...

  9. 《Android源码设计模式》--状态模式--责任链模式--解释器模式--命令模式--观察者模式--备忘录模式--迭代器模式

    [状态模式] No1: Wifi设置界面是一个叫做WifiSetting的Fragment实现的 No2: 在不同的状态下对于扫描Wifi这个请求的处理是完全不一样的.在初始状态下扫描请求被直接忽略, ...

随机推荐

  1. php与xpath使用操作文本节点

    <?php $html="<p> 对于2014年,省统计局的统计分析显示,我省消费流通领域受诸多因素的影响,有机遇也有挑战.但值得注意的是,消费增长还存在不少制约因素,比如 ...

  2. php简易灌水

    <?php $data = array ('content' => '白菜大侠','itemid'=>58); $data = http_build_query($data); $o ...

  3. 非Unicode工程读取Unicode文件

    MyUnicodeReader.h #pragma once /******************************************************************** ...

  4. 关闭Eclipse的控制台console自动跳出

    一.背景 在eclipse中进行开发,尤其是在后台有项目运行的时候,当有log或者错误需要打印到console中时,控制台就会被自动弹出,恰好这时候你又在编写代码,就会感觉瞬间想杀人,下面我们就来分享 ...

  5. ios block中引用self

    __block __weak typeof(self) tmpSelf = self; ^(){ tmpSelf...... }

  6. [Android Pro] 使用apktool工具遇到could not decode arsc file的解决办法

    转:http://www.cnblogs.com/sage-blog/p/4323049.html 最近使用APKtool工具反编译APK老是提示不成功,错误如下: Exception in thre ...

  7. iOS开发如何学习前端

    原文链接 前端大概三大块. HTML CSS JavaScript 基本上每个概念在iOS中都有对应的.HTML请想象成只能拉Autolayout或者设置Frame的ViewController.好比 ...

  8. 查询Oracle中字段名带"."的数据

    SDE中的TT_L线层会有SHAPE.LEN这样的字段,使用: SQL>select shape.len from tt_l; 或 SQL>select t.shape.len from ...

  9. java1.8中Lambda表达式reduce聚合测试例子

    public class LambdaTest { public static void main(String[] args) { // 相当于foreach遍历操作结果值 Integer out ...

  10. java ---线程wait/notify/sleep/yield/join

    一.线程的状态 Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态). New:新建状态,当线 ...