模板方法模式定义: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.定义一个方法操作算法的框架(骨架结构),而将一些步骤延迟到子类中。
模板方法模式定义:
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源码中,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)
         */

具体函数代码如下所示:

View Code

  1. /**
  2. * Manually render this view (and all of its children) to the given Canvas.
  3. * The view must have already done a full layout before this function is
  4. * called. When implementing a view, implement
  5. * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
  6. * If you do need to override this method, call the superclass version.
  7. *
  8. * @param canvas The Canvas to which the View is rendered.
  9. */
  10. public void draw(Canvas canvas) {
  11. if (ViewDebug.TRACE_HIERARCHY) {
  12. ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
  13. }
  14. final int privateFlags = mPrivateFlags;
  15. final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
  16. (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
  17. mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;
  18. /*
  19. * Draw traversal performs several drawing steps which must be executed
  20. * in the appropriate order:
  21. *
  22. * 1. Draw the background
  23. * 2. If necessary, save the canvas\' layers to prepare for fading
  24. * 3. Draw view\'s content
  25. * 4. Draw children
  26. * 5. If necessary, draw the fading edges and restore layers
  27. * 6. Draw decorations (scrollbars for instance)
  28. */
  29.  
  30. // Step 1, draw the background, if needed
  31. int saveCount;
  32. if (!dirtyOpaque) {
  33. final Drawable background = mBGDrawable;
  34. if (background != null) {
  35. final int scrollX = mScrollX;
  36. final int scrollY = mScrollY;
  37. if (mBackgroundSizeChanged) {
  38. background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
  39. mBackgroundSizeChanged = false;
  40. }
  41. if ((scrollX | scrollY) == 0) {
  42. background.draw(canvas);
  43. } else {
  44. canvas.translate(scrollX, scrollY);
  45. background.draw(canvas);
  46. canvas.translate(-scrollX, -scrollY);
  47. }
  48. }
  49. }
  50. // skip step 2 & 5 if possible (common case)
  51. final int viewFlags = mViewFlags;
  52. boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
  53. boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
  54. if (!verticalEdges && !horizontalEdges) {
  55. // Step 3, draw the content
  56. if (!dirtyOpaque) onDraw(canvas);
  57. // Step 4, draw the children
  58. dispatchDraw(canvas);
  59. // Step 6, draw decorations (scrollbars)
  60. onDrawScrollBars(canvas);
  61. // we\'re done...
  62. return;
  63. }
  64. /*
  65. * Here we do the full fledged routine...
  66. * (this is an uncommon case where speed matters less,
  67. * this is why we repeat some of the tests that have been
  68. * done above)
  69. */
  70.  
  71. boolean drawTop = false;
  72. boolean drawBottom = false;
  73. boolean drawLeft = false;
  74. boolean drawRight = false;
  75. float topFadeStrength = 0.0f;
  76. float bottomFadeStrength = 0.0f;
  77. float leftFadeStrength = 0.0f;
  78. float rightFadeStrength = 0.0f;
  79. // Step 2, save the canvas\' layers
  80. int paddingLeft = mPaddingLeft;
  81. final boolean offsetRequired = isPaddingOffsetRequired();
  82. if (offsetRequired) {
  83. paddingLeft += getLeftPaddingOffset();
  84. }
  85. int left = mScrollX + paddingLeft;
  86. int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
  87. int top = mScrollY + getFadeTop(offsetRequired);
  88. int bottom = top + getFadeHeight(offsetRequired);
  89. if (offsetRequired) {
  90. right += getRightPaddingOffset();
  91. bottom += getBottomPaddingOffset();
  92. }
  93. final ScrollabilityCache scrollabilityCache = mScrollCache;
  94. final float fadeHeight = scrollabilityCache.fadingEdgeLength;
  95. int length = (int) fadeHeight;
  96. // clip the fade length if top and bottom fades overlap
  97. // overlapping fades produce odd-looking artifacts
  98. if (verticalEdges && (top + length > bottom - length)) {
  99. length = (bottom - top) / 2;
  100. }
  101. // also clip horizontal fades if necessary
  102. if (horizontalEdges && (left + length > right - length)) {
  103. length = (right - left) / 2;
  104. }
  105. if (verticalEdges) {
  106. topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
  107. drawTop = topFadeStrength * fadeHeight > 1.0f;
  108. bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
  109. drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
  110. }
  111. if (horizontalEdges) {
  112. leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
  113. drawLeft = leftFadeStrength * fadeHeight > 1.0f;
  114. rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
  115. drawRight = rightFadeStrength * fadeHeight > 1.0f;
  116. }
  117. saveCount = canvas.getSaveCount();
  118. int solidColor = getSolidColor();
  119. if (solidColor == 0) {
  120. final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
  121. if (drawTop) {
  122. canvas.saveLayer(left, top, right, top + length, null, flags);
  123. }
  124. if (drawBottom) {
  125. canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
  126. }
  127. if (drawLeft) {
  128. canvas.saveLayer(left, top, left + length, bottom, null, flags);
  129. }
  130. if (drawRight) {
  131. canvas.saveLayer(right - length, top, right, bottom, null, flags);
  132. }
  133. } else {
  134. scrollabilityCache.setFadeColor(solidColor);
  135. }
  136. // Step 3, draw the content
  137. if (!dirtyOpaque) onDraw(canvas);
  138. // Step 4, draw the children
  139. dispatchDraw(canvas);
  140. // Step 5, draw the fade effect and restore layers
  141. final Paint p = scrollabilityCache.paint;
  142. final Matrix matrix = scrollabilityCache.matrix;
  143. final Shader fade = scrollabilityCache.shader;
  144. if (drawTop) {
  145. matrix.setScale(1, fadeHeight * topFadeStrength);
  146. matrix.postTranslate(left, top);
  147. fade.setLocalMatrix(matrix);
  148. canvas.drawRect(left, top, right, top + length, p);
  149. }
  150. if (drawBottom) {
  151. matrix.setScale(1, fadeHeight * bottomFadeStrength);
  152. matrix.postRotate(180);
  153. matrix.postTranslate(left, bottom);
  154. fade.setLocalMatrix(matrix);
  155. canvas.drawRect(left, bottom - length, right, bottom, p);
  156. }
  157. if (drawLeft) {
  158. matrix.setScale(1, fadeHeight * leftFadeStrength);
  159. matrix.postRotate(-90);
  160. matrix.postTranslate(left, top);
  161. fade.setLocalMatrix(matrix);
  162. canvas.drawRect(left, top, left + length, bottom, p);
  163. }
  164. if (drawRight) {
  165. matrix.setScale(1, fadeHeight * rightFadeStrength);
  166. matrix.postRotate(90);
  167. matrix.postTranslate(right, top);
  168. fade.setLocalMatrix(matrix);
  169. canvas.drawRect(right - length, top, right, bottom, p);
  170. }
  171. canvas.restoreToCount(saveCount);
  172. // Step 6, draw decorations (scrollbars)
  173. onDrawScrollBars(canvas);
  174. }

其中第三步( 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. if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
  9. restartMarqueeIfNeeded();
  10. // Draw the background for this view
  11. super.onDraw(canvas);
  12. final int compoundPaddingLeft = getCompoundPaddingLeft();
  13. final int compoundPaddingTop = getCompoundPaddingTop();
  14. final int compoundPaddingRight = getCompoundPaddingRight();
  15. final int compoundPaddingBottom = getCompoundPaddingBottom();
  16. final int scrollX = mScrollX;
  17. final int scrollY = mScrollY;
  18. final int right = mRight;
  19. final int left = mLeft;
  20. final int bottom = mBottom;
  21. final int top = mTop;
  22. final Drawables dr = mDrawables;
  23. if (dr != null) {
  24. /*
  25. * Compound, not extended, because the icon is not clipped
  26. * if the text height is smaller.
  27. 。。。
  28. }

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

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

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

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

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

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

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

  最后记住:

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

版权声明:本文为yemeishu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yemeishu/archive/2013/01/20/2868671.html