转载

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

大家都知道 我们在自定义view的时候 主要是复写3个函数 按顺序来 分别是 A.onMeasure() B.Layout() C.onDraw().今天就来详细分析一下 这三个函数。

首先来看measure()函数

 1  public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  2         if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||  3                 widthMeasureSpec != mOldWidthMeasureSpec ||  4                 heightMeasureSpec != mOldHeightMeasureSpec) {  5   6             // first clears the measured dimension flag  7             mPrivateFlags &= ~MEASURED_DIMENSION_SET;  8   9             if (ViewDebug.TRACE_HIERARCHY) { 10                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE); 11             } 12  13             // measure ourselves, this should set the measured dimension flag back 14             onMeasure(widthMeasureSpec, heightMeasureSpec); 15  16             // flag not set, setMeasuredDimension() was not invoked, we raise 17             // an exception to warn the developer 18             if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) { 19                 throw new IllegalStateException("onMeasure() did not set the" 20                         + " measured dimension by calling" 21                         + " setMeasuredDimension()"); 22             } 23  24             mPrivateFlags |= LAYOUT_REQUIRED; 25         } 26  27         mOldWidthMeasureSpec = widthMeasureSpec; 28         mOldHeightMeasureSpec = heightMeasureSpec; 29     }

在这里可以很清楚的看到他是final函数 子类是不能复写他的!所以他的过程是不变的,唯一可以改变的是onMeasure()这个函数!

同时我们在这个地方要注意 widthMeasureSpec和heightMeasureSpec 一般都是父视图来传递这个值的!所以我们在这个地方

就可以理解对于子视图来说 他的大小 一定程度上是由父视图来决定的。当然了,这个地方有人就要问了,那最顶层的view这2个

值又是谁给的呢?我们都知道view的绘制流程里是从viewroot这个类的performTraversals函数来开始的,我们就去看看这个函数(代码太多 我只截图截关键的)

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

可以看到这边又调用了getroot这个方法 我们再看看这个方法

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

到这边大家就能看的很清楚了,顶层view的大小实际上就是根据viewgroup的 参数来决定的,其实这个地方想想就能明白了,android里面view是不可能脱离viewgroup而独立存在的,

所以最顶层的部分一定是一个viewgroup!而这个viewgroup 在xml里的layout属性 就决定了传给下层的meaurespec的值了!

这个地方看下源码也能清晰的得出一个结论 当rootDimension为 match 属性 spec的mode 就为exactly  而为wrap 属性的时候 spec的mode就是at most。

而且我们能看出来 对于顶层来说他的size就是windowsize!换句话说 就是全屏的!

1 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 2         setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 3                 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 4     }

这2个int类型的参数值我们可以理解成为是 view 本身自己想要的大小。但是要注意这个地方是自己想要,最终要的结果是什么 不一定。

而这个measurespec的值  需要2个另外的值来构建他,一个是siez 还有一个是mode。这三者之间的关系 实际上就是view的一个内部类来决定的。

 1  public static class MeasureSpec {  2         private static final int MODE_SHIFT = 30;  3         private static final int MODE_MASK  = 0x3 << MODE_SHIFT;  4   5         /**  6          * Measure specification mode: The parent has not imposed any constraint  7          * on the child. It can be whatever size it wants.  8          */  9         public static final int UNSPECIFIED = 0 << MODE_SHIFT; 10  11         /** 12          * Measure specification mode: The parent has determined an exact size 13          * for the child. The child is going to be given those bounds regardless 14          * of how big it wants to be. 15          */ 16         public static final int EXACTLY     = 1 << MODE_SHIFT; 17  18         /** 19          * Measure specification mode: The child can be as large as it wants up 20          * to the specified size. 21          */ 22         public static final int AT_MOST     = 2 << MODE_SHIFT; 23  24         /** 25          * Creates a measure specification based on the supplied size and mode. 26          * 27          * The mode must always be one of the following: 28          * <ul> 29          *  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> 30          *  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> 31          *  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> 32          * </ul> 33          * 34          * @param size the size of the measure specification 35          * @param mode the mode of the measure specification 36          * @return the measure specification based on size and mode 37          */ 38         public static int makeMeasureSpec(int size, int mode) { 39             return size + mode; 40         } 41  42         /** 43          * Extracts the mode from the supplied measure specification. 44          * 45          * @param measureSpec the measure specification to extract the mode from 46          * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, 47          *         {@link android.view.View.MeasureSpec#AT_MOST} or 48          *         {@link android.view.View.MeasureSpec#EXACTLY} 49          */ 50         public static int getMode(int measureSpec) { 51             return (measureSpec & MODE_MASK); 52         } 53  54         /** 55          * Extracts the size from the supplied measure specification. 56          * 57          * @param measureSpec the measure specification to extract the size from 58          * @return the size in pixels defined in the supplied measure specification 59          */ 60         public static int getSize(int measureSpec) { 61             return (measureSpec & ~MODE_MASK); 62         } 63  64         /** 65          * Returns a String representation of the specified measure 66          * specification. 67          * 68          * @param measureSpec the measure specification to convert to a String 69          * @return a String with the following format: "MeasureSpec: MODE SIZE" 70          */ 71         public static String toString(int measureSpec) { 72             int mode = getMode(measureSpec); 73             int size = getSize(measureSpec); 74  75             StringBuilder sb = new StringBuilder("MeasureSpec: "); 76  77             if (mode == UNSPECIFIED) 78                 sb.append("UNSPECIFIED "); 79             else if (mode == EXACTLY) 80                 sb.append("EXACTLY "); 81             else if (mode == AT_MOST) 82                 sb.append("AT_MOST "); 83             else 84                 sb.append(mode).append(" "); 85  86             sb.append(size); 87             return sb.toString(); 88         } 89     }

这个地方的源码 可以详细帮助大家分析一下,很多人都没彻底弄明白这个地方为什么要这么写。

首先看第三行:0x3 代表16进制,换算成二进制就是11。 然后他还0x3 << MODE_SHIFT ,注意看这个地方就是左移了30位,那这个地方MODE_MASK 的值就是

11 00000 00000 00000 00000 00000 00000  注意这个地方一共就是32位了!为什么要这么处理呢,因为32位就是 int类型的大小! 所以这个地方mode_mask

的作用就是起一个遮罩的作用,这个方法在android 源码里面是很常见的,实际上这个地方就是用1来标注需要的值 0就是标注不需要的值!!!

然后我们再来看一下其他的值: 也就是3种mode的值

UNSPECIFIED 00 00000 00000 00000.。。。后面一共30个0   含义是 子视图可以得到任意他想要的大小 父视图没用 这种情况很少使用

EXACTLY       01 00000 00000 00000 ...一共30个0              含义是 父视图希望子视图的大小是specSize中指定的

AT_MOST      10 00000 00000 00000 一共30个0                 含义是  子最大可以达到的大小 比如设置成wrap content的时候 就是这个值了

38- 40行 makeMeasureSpec这个函数 我们看到这个地方是把size和mode 的值给加起来,这个地方实际上是一个二进制 加法,32位和31位 就代表了mode的值,其余30位代表size的值。

50-52 行 getmode这个函数 ,这个地方就是用我们的measurespec 和mode mask做与运算  因为1只有和1 在一起做与运算才是1 ,所以函数的返回值 就一定是measurespec的 mode的值!(0和1与是0 1和1与还是1)

60-62行 getsize这个函数   原理和上面其实是一样的,只不过把mode mask做了取反就变成了00 1111。。(30个1 )所以这个地方当然可以取得measurespec的 后30位也就是size的值了!

然后再回头来看onMeasure 这个函数,是调用了setMeasuredDimension这个方法,

1 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { 2         mMeasuredWidth = measuredWidth; 3         mMeasuredHeight = measuredHeight; 4  5         mPrivateFlags |= MEASURED_DIMENSION_SET; 6     }

这个方法大家可以看出来就是给view的 宽高赋值的!这个方法是权限最高的方法 如果你在这个方法传入是什么值 这个view最终就是什么大小。!!!!!!!!!!你设置任何其他的属性都将不启作用。

不过我们在自定义view的时候 最好还是根据子视图的measureSpec来设置view的大小 ,这样写法比较好,可以尊重其他程序员在xml里设置的属性!

而setMeasuredDimension这个方法在onMeasure里面是调用了getDefaultSize 这个方法的。

 1  /**  2      * Utility to return a default size. Uses the supplied size if the  3      * MeasureSpec imposed no constraints. Will get larger if allowed  4      * by the MeasureSpec.  5      *  6      * @param size Default size for this view  7      * @param measureSpec Constraints imposed by the parent  8      * @return The size this view should be.  9      */ 10     public static int getDefaultSize(int size, int measureSpec) { 11         int result = size; 12         int specMode = MeasureSpec.getMode(measureSpec); 13         int specSize = MeasureSpec.getSize(measureSpec); 14  15         switch (specMode) { 16         case MeasureSpec.UNSPECIFIED: 17             result = size; 18             break; 19         case MeasureSpec.AT_MOST: 20         case MeasureSpec.EXACTLY: 21             result = specSize; 22             break; 23         } 24         return result; 25     }

这个函数的作用就是返回一个默认的值,第一个参数size 就是提供的默认的大小 第二个参数是测量的大小

注意测量的大小是顶层传下来的,如果specMode等于AT_MOST或EXACTLY就返回specSize,这也是系统默认的行为。之后会在onMeasure()方法中调用setMeasuredDimension()方法来设定测量出的大小,

这样一次measure的过程就结束了。当然了对于一个复杂的界面来说 一个布局里面肯定是嵌套了很多层,比如一个viewgroup有好多个字view 就肯定就是多次调用这个measure的过程了。看下面这个函数就能知道

 1 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {  2         final int size = mChildrenCount;  3         final View[] children = mChildren;  4         for (int i = 0; i < size; ++i) {  5             final View child = children[i];  6             if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {  7                 measureChild(child, widthMeasureSpec, heightMeasureSpec);  8             }  9         } 10     }

再跟进去看就是

 1 protected void measureChild(View child, int parentWidthMeasureSpec,  2             int parentHeightMeasureSpec) {  3         final LayoutParams lp = child.getLayoutParams();  4   5         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  6                 mPaddingLeft + mPaddingRight, lp.width);  7         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  8                 mPaddingTop + mPaddingBottom, lp.height);  9  10         child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 11     }

再跟进去看getchild这个方法

 1 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {  2         int specMode = MeasureSpec.getMode(spec);  3         int specSize = MeasureSpec.getSize(spec);  4   5         int size = Math.max(0, specSize - padding);  6   7         int resultSize = 0;  8         int resultMode = 0;  9  10         switch (specMode) { 11         // Parent has imposed an exact size on us 12         case MeasureSpec.EXACTLY: 13             if (childDimension >= 0) { 14                 resultSize = childDimension; 15                 resultMode = MeasureSpec.EXACTLY; 16             } else if (childDimension == LayoutParams.MATCH_PARENT) { 17                 // Child wants to be our size. So be it. 18                 resultSize = size; 19                 resultMode = MeasureSpec.EXACTLY; 20             } else if (childDimension == LayoutParams.WRAP_CONTENT) { 21                 // Child wants to determine its own size. It can't be 22                 // bigger than us. 23                 resultSize = size; 24                 resultMode = MeasureSpec.AT_MOST; 25             } 26             break; 27  28         // Parent has imposed a maximum size on us 29         case MeasureSpec.AT_MOST: 30             if (childDimension >= 0) { 31                 // Child wants a specific size... so be it 32                 resultSize = childDimension; 33                 resultMode = MeasureSpec.EXACTLY; 34             } else if (childDimension == LayoutParams.MATCH_PARENT) { 35                 // Child wants to be our size, but our size is not fixed. 36                 // Constrain child to not be bigger than us. 37                 resultSize = size; 38                 resultMode = MeasureSpec.AT_MOST; 39             } else if (childDimension == LayoutParams.WRAP_CONTENT) { 40                 // Child wants to determine its own size. It can't be 41                 // bigger than us. 42                 resultSize = size; 43                 resultMode = MeasureSpec.AT_MOST; 44             } 45             break; 46  47         // Parent asked to see how big we want to be 48         case MeasureSpec.UNSPECIFIED: 49             if (childDimension >= 0) { 50                 // Child wants a specific size... let him have it 51                 resultSize = childDimension; 52                 resultMode = MeasureSpec.EXACTLY; 53             } else if (childDimension == LayoutParams.MATCH_PARENT) { 54                 // Child wants to be our size... find out how big it should 55                 // be 56                 resultSize = 0; 57                 resultMode = MeasureSpec.UNSPECIFIED; 58             } else if (childDimension == LayoutParams.WRAP_CONTENT) { 59                 // Child wants to determine its own size.... find out how 60                 // big it should be 61                 resultSize = 0; 62                 resultMode = MeasureSpec.UNSPECIFIED; 63             } 64             break; 65         } 66         return MeasureSpec.makeMeasureSpec(resultSize, resultMode); 67     }

getChildMeasureSpec实际上就是父视图给了measurespec这个参数,就拿这个参数算出specmode和specsieze。然后根据计算出来的specmode和子视图的 childdimension(这个就是宽高的值) 来计算自身的spec,

如果自己包含子视图 就把这个值 传给子视图的measure函数 同时也作为自身调用setMeasuredDimension的参数,如果不包含子视图 那就直接调用onMeasur的默认实现了。当然最终也是要调用到setMeasureDimesion这个函数的

到这里 确定view大小的工作就完成了,确定大小以后我们肯定要确定位置,

 1 public void layout(int l, int t, int r, int b) {  2         int oldL = mLeft;  3         int oldT = mTop;  4         int oldB = mBottom;  5         int oldR = mRight;  6         boolean changed = setFrame(l, t, r, b);  7         if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {  8             if (ViewDebug.TRACE_HIERARCHY) {  9                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT); 10             } 11  12             onLayout(changed, l, t, r, b); 13             mPrivateFlags &= ~LAYOUT_REQUIRED; 14  15             if (mOnLayoutChangeListeners != null) { 16                 ArrayList<OnLayoutChangeListener> listenersCopy = 17                         (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone(); 18                 int numListeners = listenersCopy.size(); 19                 for (int i = 0; i < numListeners; ++i) { 20                     listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); 21                 } 22             } 23         } 24         mPrivateFlags &= ~FORCE_LAYOUT; 25     }

这个地方也很简单,第六行就是看是否有变化 有变化就重新放位置 没有变化 就什么也不做,看下onlayout这个方法

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {     }

在这发现是个空方法,也就是说onMeasure是有默认行为的 而onlayout则没有,因为子view的放置位置一定是由父视图来决定的,

所以我们当然要去看看viewgroup的这个方法

1  protected abstract void onLayout(boolean changed, 2             int l, int t, int r, int b);

可以看到 是个抽象方法 这就意味着只要你想写viewgroup 那这个方法你必须重写,有兴趣的同学可以看看android 系统自带的哪些viewgroup是不是都实现了这个方法。

实际上这个onlayout在我们自定义viewgroup的时候 是最难处理的地方也是技巧含量最高的地方(但是自定义view是不需要处理这个地方的) 今后我会分析一些开源控件的viewgroup来带着大家好好理解这个函数

最后我们来看一下draw方法

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

这个函数 其实谷歌自己的注释已经能够让我们看懂了,简单提一下

23行-45行 第一步 就是绘制这个view的背景,background 这个我们在xml里可以直接设置。

155行 调用了ondraw方法

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     }

这个地方实际上是一个空方法 注释也告诉你了 实际上你要自定义view的时候 主要就是重写这个方法

158行 调用了这个方法

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     }

也是一个空方法,而且注释告诉我们 只有有子view 也就是viewgroup类型的时候 才需要去重写这个方法。有兴趣的可以去看一下viewgroup的这个函数 内容。

199行,也就是最后一行,绘制滚动条的,其实view 都是有滚动条的,只不过你可以选择显示他还是不显示他 罢了。

一直到这里我们view的绘制流程 就基本讲清楚了,当然还没有结束 因为还有重绘的流程。

这个地方要注意 view的invalidate函数有好多种 我们只取其中的某一种来分析,其他可以自己分析 大体上都差不多

 1 public void invalidate() {  2         invalidate(true);  3     }  4   5     /**  6      * This is where the invalidate() work actually happens. A full invalidate()  7      * causes the drawing cache to be invalidated, but this function can be called with  8      * invalidateCache set to false to skip that invalidation step for cases that do not  9      * need it (for example, a component that remains at the same dimensions with the same 10      * content). 11      * 12      * @param invalidateCache Whether the drawing cache for this view should be invalidated as 13      * well. This is usually true for a full invalidate, but may be set to false if the 14      * View's contents or dimensions have not changed. 15      */ 16     void invalidate(boolean invalidateCache) { 17         if (ViewDebug.TRACE_HIERARCHY) { 18             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); 19         } 20  21         if (skipInvalidate()) { 22             return; 23         } 24         if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || 25                 (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) || 26                 (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) { 27             mLastIsOpaque = isOpaque(); 28             mPrivateFlags &= ~DRAWN; 29             mPrivateFlags |= DIRTY; 30             if (invalidateCache) { 31                 mPrivateFlags |= INVALIDATED; 32                 mPrivateFlags &= ~DRAWING_CACHE_VALID; 33             } 34             final AttachInfo ai = mAttachInfo; 35             final ViewParent p = mParent; 36             //noinspection PointlessBooleanExpression,ConstantConditions 37             if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { 38                 if (p != null && ai != null && ai.mHardwareAccelerated) { 39                     // fast-track for GL-enabled applications; just invalidate the whole hierarchy 40                     // with a null dirty rect, which tells the ViewAncestor to redraw everything 41                     p.invalidateChild(this, null); 42                     return; 43                 } 44             } 45  46             if (p != null && ai != null) { 47                 final Rect r = ai.mTmpInvalRect; 48                 r.set(0, 0, mRight - mLeft, mBottom - mTop); 49                 // Don't call invalidate -- we don't want to internally scroll 50                 // our own bounds 51                 p.invalidateChild(this, r); 52             } 53         } 54     }

我们首先来看一下21行调用的函数,

1 ** 2      * Do not invalidate views which are not visible and which are not running an animation. They 3      * will not get drawn and they should not set dirty flags as if they will be drawn 4      */ 5     private boolean skipInvalidate() { 6         return (mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null && 7                 (!(mParent instanceof ViewGroup) || 8                         !((ViewGroup) mParent).isViewTransitioning(this)); 9     }

注释说的很明确 就是判断一下这个view 可视状态是否发生了变化,另外是否正在运行动画。如果都没有 那就返回true 这样invalidate函数就不会继续执行

41 行 和51行 调用了invalidateChild 这个方法,然后我们看那个p 实际上就是当前view的 parentview,也就是viewgroup

哪我们就去看看viewgroup的invalidatechild方法

  1 /**   2      * Don't call or override this method. It is used for the implementation of   3      * the view hierarchy.   4      */   5     public final void invalidateChild(View child, final Rect dirty) {   6         if (ViewDebug.TRACE_HIERARCHY) {   7             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);   8         }   9   10         ViewParent parent = this;  11   12         final AttachInfo attachInfo = mAttachInfo;  13         if (attachInfo != null) {  14             // If the child is drawing an animation, we want to copy this flag onto  15             // ourselves and the parent to make sure the invalidate request goes  16             // through  17             final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;  18   19             if (dirty == null) {  20                 if (child.mLayerType != LAYER_TYPE_NONE) {  21                     mPrivateFlags |= INVALIDATED;  22                     mPrivateFlags &= ~DRAWING_CACHE_VALID;  23                     child.mLocalDirtyRect.setEmpty();  24                 }  25                 do {  26                     View view = null;  27                     if (parent instanceof View) {  28                         view = (View) parent;  29                         if (view.mLayerType != LAYER_TYPE_NONE) {  30                             view.mLocalDirtyRect.setEmpty();  31                             if (view.getParent() instanceof View) {  32                                 final View grandParent = (View) view.getParent();  33                                 grandParent.mPrivateFlags |= INVALIDATED;  34                                 grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID;  35                             }  36                         }  37                         if ((view.mPrivateFlags & DIRTY_MASK) != 0) {  38                             // already marked dirty - we're done  39                             break;  40                         }  41                     }  42   43                     if (drawAnimation) {  44                         if (view != null) {  45                             view.mPrivateFlags |= DRAW_ANIMATION;  46                         } else if (parent instanceof ViewRootImpl) {  47                             ((ViewRootImpl) parent).mIsAnimating = true;  48                         }  49                     }  50   51                     if (parent instanceof ViewRootImpl) {  52                         ((ViewRootImpl) parent).invalidate();  53                         parent = null;  54                     } else if (view != null) {  55                         if ((view.mPrivateFlags & DRAWN) == DRAWN ||  56                                 (view.mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {  57                             view.mPrivateFlags &= ~DRAWING_CACHE_VALID;  58                             view.mPrivateFlags |= DIRTY;  59                             parent = view.mParent;  60                         } else {  61                             parent = null;  62                         }  63                     }  64                 } while (parent != null);  65             } else {  66                 // Check whether the child that requests the invalidate is fully opaque  67                 final boolean isOpaque = child.isOpaque() && !drawAnimation &&  68                         child.getAnimation() == null;  69                 // Mark the child as dirty, using the appropriate flag  70                 // Make sure we do not set both flags at the same time  71                 int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;  72   73                 if (child.mLayerType != LAYER_TYPE_NONE) {  74                     mPrivateFlags |= INVALIDATED;  75                     mPrivateFlags &= ~DRAWING_CACHE_VALID;  76                     child.mLocalDirtyRect.union(dirty);  77                 }  78   79                 final int[] location = attachInfo.mInvalidateChildLocation;  80                 location[CHILD_LEFT_INDEX] = child.mLeft;  81                 location[CHILD_TOP_INDEX] = child.mTop;  82                 Matrix childMatrix = child.getMatrix();  83                 if (!childMatrix.isIdentity()) {  84                     RectF boundingRect = attachInfo.mTmpTransformRect;  85                     boundingRect.set(dirty);  86                     //boundingRect.inset(-0.5f, -0.5f);  87                     childMatrix.mapRect(boundingRect);  88                     dirty.set((int) (boundingRect.left - 0.5f),  89                             (int) (boundingRect.top - 0.5f),  90                             (int) (boundingRect.right + 0.5f),  91                             (int) (boundingRect.bottom + 0.5f));  92                 }  93   94                 do {  95                     View view = null;  96                     if (parent instanceof View) {  97                         view = (View) parent;  98                         if (view.mLayerType != LAYER_TYPE_NONE &&  99                                 view.getParent() instanceof View) { 100                             final View grandParent = (View) view.getParent(); 101                             grandParent.mPrivateFlags |= INVALIDATED; 102                             grandParent.mPrivateFlags &= ~DRAWING_CACHE_VALID; 103                         } 104                     } 105  106                     if (drawAnimation) { 107                         if (view != null) { 108                             view.mPrivateFlags |= DRAW_ANIMATION; 109                         } else if (parent instanceof ViewRootImpl) { 110                             ((ViewRootImpl) parent).mIsAnimating = true; 111                         } 112                     } 113  114                     // If the parent is dirty opaque or not dirty, mark it dirty with the opaque 115                     // flag coming from the child that initiated the invalidate 116                     if (view != null) { 117                         if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && 118                                 view.getSolidColor() == 0) { 119                             opaqueFlag = DIRTY; 120                         } 121                         if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) { 122                             view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; 123                         } 124                     } 125  126                     parent = parent.invalidateChildInParent(location, dirty); 127                     if (view != null) { 128                         // Account for transform on current parent 129                         Matrix m = view.getMatrix(); 130                         if (!m.isIdentity()) { 131                             RectF boundingRect = attachInfo.mTmpTransformRect; 132                             boundingRect.set(dirty); 133                             m.mapRect(boundingRect); 134                             dirty.set((int) boundingRect.left, (int) boundingRect.top, 135                                     (int) (boundingRect.right + 0.5f), 136                                     (int) (boundingRect.bottom + 0.5f)); 137                         } 138                     } 139                 } while (parent != null); 140             } 141         } 142     }

这个地方实际上就是在不断的判断是否有父控件,然后调用父控件的invalidateChildInParent 方法。

我们都知道最顶层的东西还是那个viewroot ,我们去看看这个方法

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

Android自定义view教程06--view的绘制流程简单分析(基于android 4.0源码进行分析)

上面四张图 就是viewroot里面的调用链,按顺序看下来 实际上最后一步就是调用的performtraversals这个方法,到这个地方

再往下就是 measure layout draw 那一套前面我们讲过的循环了!

到这里 这一章的内容就暂时结束了,算了帮助大家从源码角度过了一遍view 的绘制和 重绘的流程,当然这个过程要加深理解

还是要自己多动手写自定义控件或者分析别人的开源控件来进行提高,08我们会补充讲述一下 android里面的事件分发 触摸事件什么的。

这样 动画,自定义view,触摸滑动事件 这三大块讲完 就可以分析3-5个开源控件的源码 来加深对这一块的理解。

正文到此结束
Loading...