一起Talk Android吧(第一百四十九回:Android自定义View之Draw一)

各位看官们大家好,上一回中咱们说的是Android中自定义View之Layout的例子,这一回咱们说的例子是自定义View之Draw。闲话休提,言归正转。让我们一起Talk Android吧!

看官们,我们在前面章回中介绍了Measure和Layout的细节和流程,在接下来的章回中将介绍Draw的细节和流程,其实我们在前面章回中介绍过Draw的流程,在这里我们对其做进一步的完善:

doTraversal()->performTraversals()->performDraw()->draw()->drawSoftware()->draw()->onDraw()

该流程中前五个函数在ViewRootImpl.java文件中,后两个函数在View.java文件中(这点和Measure以及Layout流程类似)。明白流程后我们重点看看draw函数的源代码,具体如下:


  /**
  * Manually render this view (and all of its children) to the given Canvas.
  * The view must have already done a full layout before this function is
  * called.  When implementing a view, implement
  * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
  * If you do need to override this method, call the superclass version.
  *
  * @param canvas The Canvas to which the View is rendered.
  */
 @CallSuper
 public void draw(Canvas canvas) {
     final int privateFlags = mPrivateFlags;
     final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
             (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
     mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

     /*
      * 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)
      */

     // Step 1, draw the background, if needed
     int saveCount;

     if (!dirtyOpaque) {
         drawBackground(canvas);
     }

     // skip step 2 & 5 if possible (common case)
     final int viewFlags = mViewFlags;
     boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
     boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
     if (!verticalEdges && !horizontalEdges) {
         // Step 3, draw the content
         if (!dirtyOpaque) onDraw(canvas);

         // Step 4, draw the children
         dispatchDraw(canvas);

         drawAutofilledHighlight(canvas);

         // Overlay is part of the content and draws beneath Foreground
         if (mOverlay != null && !mOverlay.isEmpty()) {
             mOverlay.getOverlayView().dispatchDraw(canvas);
         }

         // Step 6, draw decorations (foreground, scrollbars)
         onDrawForeground(canvas);

         // Step 7, draw the default focus highlight
         drawDefaultFocusHighlight(canvas);

         if (debugDraw()) {
             debugDrawFocus(canvas);
         }

         // we're done...
         return;
     }

     /*
      * Here we do the full fledged routine...
      * (this is an uncommon case where speed matters less,
      * this is why we repeat some of the tests that have been
      * done above)
      */

     boolean drawTop = false;
     boolean drawBottom = false;
     boolean drawLeft = false;
     boolean drawRight = false;

     float topFadeStrength = 0.0f;
     float bottomFadeStrength = 0.0f;
     float leftFadeStrength = 0.0f;
     float rightFadeStrength = 0.0f;

     // Step 2, save the canvas' layers
     int paddingLeft = mPaddingLeft;

     final boolean offsetRequired = isPaddingOffsetRequired();
     if (offsetRequired) {
         paddingLeft += getLeftPaddingOffset();
     }

     int left = mScrollX + paddingLeft;
     int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
     int top = mScrollY + getFadeTop(offsetRequired);
     int bottom = top + getFadeHeight(offsetRequired);

     if (offsetRequired) {
         right += getRightPaddingOffset();
         bottom += getBottomPaddingOffset();
     }

     final ScrollabilityCache scrollabilityCache = mScrollCache;
     final float fadeHeight = scrollabilityCache.fadingEdgeLength;
     int length = (int) fadeHeight;

     // clip the fade length if top and bottom fades overlap
     // overlapping fades produce odd-looking artifacts
     if (verticalEdges && (top + length > bottom - length)) {
         length = (bottom - top) / 2;
     }

     // also clip horizontal fades if necessary
     if (horizontalEdges && (left + length > right - length)) {
         length = (right - left) / 2;
     }

     if (verticalEdges) {
         topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
         drawTop = topFadeStrength * fadeHeight > 1.0f;
         bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
         drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
     }

     if (horizontalEdges) {
         leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
         drawLeft = leftFadeStrength * fadeHeight > 1.0f;
         rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
         drawRight = rightFadeStrength * fadeHeight > 1.0f;
     }

     saveCount = canvas.getSaveCount();

     int solidColor = getSolidColor();
     if (solidColor == 0) {
         if (drawTop) {
             canvas.saveUnclippedLayer(left, top, right, top + length);
         }

         if (drawBottom) {
             canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
         }

         if (drawLeft) {
             canvas.saveUnclippedLayer(left, top, left + length, bottom);
         }

         if (drawRight) {
             canvas.saveUnclippedLayer(right - length, top, right, bottom);
         }
     } else {
         scrollabilityCache.setFadeColor(solidColor);
     }

     // Step 3, draw the content
     if (!dirtyOpaque) onDraw(canvas);

     // Step 4, draw the children
     dispatchDraw(canvas);

     // Step 5, draw the fade effect and restore layers
     final Paint p = scrollabilityCache.paint;
     final Matrix matrix = scrollabilityCache.matrix;
     final Shader fade = scrollabilityCache.shader;

     if (drawTop) {
         matrix.setScale(1, fadeHeight * topFadeStrength);
         matrix.postTranslate(left, top);
         fade.setLocalMatrix(matrix);
         p.setShader(fade);
         canvas.drawRect(left, top, right, top + length, p);
     }

     if (drawBottom) {
         matrix.setScale(1, fadeHeight * bottomFadeStrength);
         matrix.postRotate(180);
         matrix.postTranslate(left, bottom);
         fade.setLocalMatrix(matrix);
         p.setShader(fade);
         canvas.drawRect(left, bottom - length, right, bottom, p);
     }

     if (drawLeft) {
         matrix.setScale(1, fadeHeight * leftFadeStrength);
         matrix.postRotate(-90);
         matrix.postTranslate(left, top);
         fade.setLocalMatrix(matrix);
         p.setShader(fade);
         canvas.drawRect(left, top, left + length, bottom, p);
     }

     if (drawRight) {
         matrix.setScale(1, fadeHeight * rightFadeStrength);
         matrix.postRotate(90);
         matrix.postTranslate(right, top);
         fade.setLocalMatrix(matrix);
         p.setShader(fade);
         canvas.drawRect(right - length, top, right, bottom, p);
     }

     canvas.restoreToCount(saveCount);

     drawAutofilledHighlight(canvas);

     // Overlay is part of the content and draws beneath Foreground
     if (mOverlay != null && !mOverlay.isEmpty()) {
         mOverlay.getOverlayView().dispatchDraw(canvas);
     }

     // Step 6, draw decorations (foreground, scrollbars)
     onDrawForeground(canvas);

     if (debugDraw()) {
         debugDrawFocus(canvas);
     }
 }

从上面的代码中可以看出draw的过程主要分为六个步骤,其中第二步和第五不是必须的。代码中对此给了注释,相信大家结合注释就能明白具体的流程。

接下来我们看看onDraw()函数的代码,不过发现它是个空函数,他把draw操作留给了具体的子类去实现,我想这也是官方建议我们去重写onDraw()方法的原因。

  /**
   * Implement this to do your drawing.
   *
   * @param canvas the canvas on which the background will be drawn
   */
  protected void onDraw(Canvas canvas) {
  }

另外,补充一下View类中有两个draw()它们是重载关系,这点从函数的参数数量上就能看出来,一个函数有一个参数,另外一个有三个参数,我们在这里只介绍有一个参数的函数,有三个参数的函数以后章回中会介绍。

各位看官,关于Androd中自定义View之Draw的例子咱们就介绍到这里,欲知后面还有什么例子,且听下回分解!

你可能感兴趣的:(Android,一起Talk,Android吧)