Android中view繪製過程
1 背景
看見沒有,如上圖中id為content的內容就是整個View樹的結構,所以對每個具體View物件的操作,其實就是個遞迴的實現。
前面《Android觸控式螢幕事件派發機制詳解與原始碼分析一(View篇)》文章的3-1小節說過Android中的任何一個佈局、任何一個控制元件其實都是直接或間接繼承自View實現的,當然也包括我們後面一步一步引出的自定義控制元件也不例外,所以說這些View應該都具有相同的繪製流程與機制才能顯示到螢幕上(因為他們都具備相同的父類View,可能每個控制元件的具體繪製邏輯有差異,但是主流程都是一樣的)。經過總結髮現每一個View的繪製過程都必須經歷三個最主要的過程,也就是measure、layout和draw。
既然一個View的繪製主要流程是這三步,那一定有一個開始地方呀,就像一個類從main函式執行一樣呀。對於View的繪製開始調運地方這裡先給出結論,本文後面會反過來分析原因的,先往下看就行。具體結論如下:
整個View樹的繪圖流程是在ViewRootImpl類的performTraversals()方法(這個方法巨長)開始的,該函式做的執行過程主要是根據之前設定的狀態,判斷是否重新計算檢視大小(measure)、是否重新放置檢視的位置(layout)、以及是否重繪 (draw),其核心也就是通過判斷來選擇順序執行這三個方法中的哪個,如下:
private void performTraversals() { ...... //最外層的根檢視的widthMeasureSpec和heightMeasureSpec由來 //lp.width和lp.height在建立ViewGroup例項時等於MATCH_PARENT int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ...... mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... }
/** * Figures out the measure spec for the root view in a window based on it's * layout params. * * @param windowSize * The available width or height of the window * * @param rootDimension * The layout params for one dimension (width or height) of the * window. * * @return The measure spec to use to measure the root view. */ private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; ...... } return measureSpec; }
可以看見這個方法的註釋說是用來測Root View的。上面傳入引數後這個函式走的是MATCH_PARENT,使用MeasureSpec.makeMeasureSpec方法組裝一個MeasureSpec,MeasureSpec的specMode等於EXACTLY,specSize等於windowSize,也就是為何根檢視總是全屏的原因。
其中的mView就是View物件。如下就是整個流程的大致流程圖:
如下我們就依據View繪製的這三個主要流程進行詳細剖析(基於Android5.1.1 API 22原始碼進行分析)。
2 View繪製流程第一步:遞迴measure原始碼分析
整個View樹的原始碼measure流程圖如下:
2-1 measure原始碼分析
先看下View的measure方法原始碼,如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * <p> * This is called to find out how big a view should be. The parent * supplies constraint information in the width and height parameters. * </p> * * <p> * The actual measurement work of a view is performed in * {@link #onMeasure(int, int)}, called by this method. Therefore, only * {@link #onMeasure(int, int)} can and must be overridden by subclasses. * </p> * * *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> widthMeasureSpec Horizontal space requirements as imposed by the * parent *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> heightMeasureSpec Vertical space requirements as imposed by the * parent * *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> #onMeasure(int, int) */</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//final方法,子類不可重寫</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">measure</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> widthMeasureSpec, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> heightMeasureSpec) { ...... <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//回撥onMeasure()方法</span> onMeasure(widthMeasureSpec, heightMeasureSpec); ...... }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li></ul>
看見註釋資訊沒有,他告訴你了很多重要資訊。為整個View樹計算實際的大小,然後設定實際的高和寬,每個View控制元件的實際寬高都是由父檢視和自身決定的。實際的測量是在onMeasure方法進行,所以在View的子類需要重寫onMeasure方法,這是因為measure方法是final的,不允許過載,所以View子類只能通過過載onMeasure來實現自己的測量邏輯。
這個方法的兩個引數都是父View傳遞過來的,也就是代表了父view的規格。他由兩部分組成,高2位表示MODE,定義在MeasureSpec類(View的內部類)中,有三種類型,MeasureSpec.EXACTLY表示確定大小, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不確定。低30位表示size,也就是父View的大小。對於系統Window類的DecorVIew物件Mode一般都為MeasureSpec.EXACTLY ,而size分別對應螢幕寬高。對於子View來說大小是由父View和子View共同決定的。
在這裡可以看出measure方法最終回調了View的onMeasure方法,我們來看下View的onMeasure原始碼,如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * <p> * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overriden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * * <p> * <strong>CONTRACT:</strong> When overriding this method, you * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the * measured width and height of this view. Failure to do so will trigger an * <code>IllegalStateException</code>, thrown by * {@link #measure(int, int)}. Calling the superclass' * {@link #onMeasure(int, int)} is a valid use. * </p> * * <p> * The base class implementation of measure defaults to the background size, * unless a larger size is allowed by the MeasureSpec. Subclasses should * override {@link #onMeasure(int, int)} to provide better measurements of * their content. * </p> * * <p> * If this method is overridden, it is the subclass's responsibility to make * sure the measured height and width are at least the view's minimum height * and width ({@link #getSuggestedMinimumHeight()} and * {@link #getSuggestedMinimumWidth()}). * </p> * *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> widthMeasureSpec horizontal space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> heightMeasureSpec vertical space requirements as imposed by the parent. * The requirements are encoded with * {@link android.view.View.MeasureSpec}. * *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> #getMeasuredWidth() *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> #getMeasuredHeight() *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> #setMeasuredDimension(int, int) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> #getSuggestedMinimumHeight() *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> #getSuggestedMinimumWidth() *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> android.view.View.MeasureSpec#getMode(int) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @see</span> android.view.View.MeasureSpec#getSize(int) */</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//View的onMeasure預設實現方法</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onMeasure</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> widthMeasureSpec, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li></ul>
看見沒有,其實註釋已經很詳細了(自定義View重寫該方法的指導操作註釋都有說明),不做過多解釋。
對於非ViewGroup的View而言,通過呼叫上面預設的onMeasure即可完成View的測量,當然你也可以過載onMeasure並呼叫setMeasuredDimension來設定任意大小的佈局,但一般不這麼做,因為這種做法不太好,至於為何不好,後面分析完你就明白了。
我們可以看見onMeasure預設的實現僅僅呼叫了setMeasuredDimension,setMeasuredDimension函式是一個很關鍵的函式,它對View的成員變數mMeasuredWidth和mMeasuredHeight變數賦值,measure的主要目的就是對View樹中的每個View的mMeasuredWidth和mMeasuredHeight進行賦值,所以一旦這兩個變數被賦值意味著該View的測量工作結束。既然這樣那我們就看看設定的預設尺寸大小吧,可以看見setMeasuredDimension傳入的引數都是通過getDefaultSize返回的,所以再來看下getDefaultSize方法原始碼,如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getDefaultSize</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> size, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> measureSpec) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> result = size; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//通過MeasureSpec解析獲取mode與size</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> specMode = MeasureSpec.getMode(measureSpec); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> specSize = MeasureSpec.getSize(measureSpec); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (specMode) { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MeasureSpec.UNSPECIFIED: result = size; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MeasureSpec.AT_MOST: <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MeasureSpec.EXACTLY: result = specSize; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> result; }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>
看見沒有,如果specMode等於AT_MOST或EXACTLY就返回specSize,這就是系統預設的規格。
回過頭繼續看上面onMeasure方法,其中getDefaultSize引數的widthMeasureSpec和heightMeasureSpec都是由父View傳遞進來的。getSuggestedMinimumWidth與getSuggestedMinimumHeight都是View的方法,具體如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getSuggestedMinimumWidth</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (mBackground == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getSuggestedMinimumHeight</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (mBackground == <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight()); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>
看見沒有,建議的最小寬度和高度都是由View的Background尺寸與通過設定View的miniXXX屬性共同決定的。
到此一次最基礎的元素View的measure過程就完成了。上面說了View實際是巢狀的,而且measure是遞迴傳遞的,所以每個View都需要measure。實際能夠巢狀的View一般都是ViewGroup的子類,所以在ViewGroup中定義了measureChildren, measureChild, measureChildWithMargins方法來對子檢視進行測量,measureChildren內部實質只是迴圈呼叫measureChild,measureChild和measureChildWithMargins的區別就是是否把margin和padding也作為子檢視的大小。如下我們以ViewGroup中稍微複雜的measureChildWithMargins方法來分析:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> child The child to measure *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> parentWidthMeasureSpec The width requirements for this view *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> widthUsed Extra space that has been used up by the parent * horizontally (possibly by other children of the parent) *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> parentHeightMeasureSpec The height requirements for this view *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> heightUsed Extra space that has been used up by the parent * vertically (possibly by other children of the parent) */</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">measureChildWithMargins</span>(View child, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> parentWidthMeasureSpec, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> widthUsed, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> parentHeightMeasureSpec, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> heightUsed) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//獲取子檢視的LayoutParams</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//調整MeasureSpec</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//通過這兩個引數以及子檢視本身的LayoutParams來共同決定子檢視的測量規格</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//調運子View的measure方法,子View的measure中會回撥子View的onMeasure方法</span> child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li></ul>
關於該方法的引數等說明註釋已經描述的夠清楚了。該方法就是對父檢視提供的measureSpec引數結合自身的LayoutParams引數進行了調整,然後再來呼叫child.measure()方法,具體通過方法getChildMeasureSpec來進行引數調整。所以我們繼續看下getChildMeasureSpec方法程式碼,如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">getChildMeasureSpec</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> spec, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> padding, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> childDimension) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//獲取當前Parent View的Mode和Size</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> specMode = MeasureSpec.getMode(spec); <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> specSize = MeasureSpec.getSize(spec); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//獲取Parent size與padding差值(也就是Parent剩餘大小),若差值小於0直接返回0</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> size = Math.max(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, specSize - padding); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//定義返回值儲存變數</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> resultSize = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> resultMode = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//依據當前Parent的Mode進行switch分支邏輯</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">switch</span> (specMode) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Parent has imposed an exact size on us</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//預設Root View的Mode就是EXACTLY</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">case</span> MeasureSpec.EXACTLY: <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (childDimension >= <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果child的layout_wOrh屬性在xml或者java中給予具體大於等於0的數值</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//設定child的size為真實layout_wOrh屬性值,mode為EXACTLY</span> resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (childDimension == LayoutParams.MATCH_PARENT) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果child的layout_wOrh屬性在xml或者java中給予MATCH_PARENT</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Child wants to be our size. So be it.</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//設定child的size為size,mode為EXACTLY</span> resultSize = size; resultMode = MeasureSpec.EXACTLY; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (childDimension == LayoutParams.WRAP_CONTENT) { <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果child的layout_wOrh屬性在xml或者java中給予WRAP_CONTENT</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//設定child的size為size,mode為AT_MOST</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// Child wants to determine its own size. It can't be</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// bigger than us.</span> resultSize = size; resultMode = MeasureSpec.AT_MOST; } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">break</span>; ...... <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//其他Mode分支類似</span> } <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//將mode與size通過MeasureSpec方法整合為32位整數返回</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> MeasureSpec.makeMeasureSpec(resultSize, resultMode); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li></ul>
可以看見,getChildMeasureSpec的邏輯是通過其父View提供的MeasureSpec引數得到specMode和specSize,然後根據計算出來的specMode以及子View的childDimension(layout_width或layout_height)來計算自身的measureSpec,如果其本身包含子檢視,則計算出來的measureSpec將作為呼叫其子檢視measure函式的引數,同時也作為自身呼叫setMeasuredDimension的引數,如果其不包含子檢視則預設情況下最終會呼叫onMeasure的預設實現,並最終呼叫到setMeasuredDimension。
所以可以看見onMeasure的引數其實就是這麼計算出來的。同時從上面的分析可以看出來,最終決定View的measure大小是View的setMeasuredDimension方法,所以我們可以通過setMeasuredDimension設定死值來設定View的mMeasuredWidth和mMeasuredHeight的大小,但是一個好的自定義View應該會根據子檢視的measureSpec來設定mMeasuredWidth和mMeasuredHeight的大小,這樣的靈活性更大,所以這也就是上面分析onMeasure時說View的onMeasure最好不要重寫死值的原因。
可以看見當通過setMeasuredDimension方法最終設定完成View的measure之後View的mMeasuredWidth和mMeasuredHeight成員才會有具體的數值,所以如果我們自定義的View或者使用現成的View想通過getMeasuredWidth()和getMeasuredHeight()方法來獲取View測量的寬高,必須保證這兩個方法在onMeasure流程之後被呼叫才能返回有效值。
還記得前面《Android應用setContentView與LayoutInflater載入解析機制原始碼分析》文章3-3小節探討的inflate方法載入一些佈局顯示時指定的大小失效問題嗎?當時只給出了結論,現在給出了詳細原因分析,我想不需要再做過多解釋了吧。
至此整個View繪製流程的第一步就分析完成了,可以看見,相對來說還是比較複雜的,接下來進行小結。
2-2 measure原理總結
通過上面分析可以看出measure過程主要就是從頂層父View向子View遞迴呼叫view.measure方法(measure中又回撥onMeasure方法)的過程。具體measure核心主要有如下幾點:
- MeasureSpec(View的內部類)測量規格為int型,值由高2位規格模式specMode和低30位具體尺寸specSize組成。其中specMode只有三種值:
<strong><code class="language-txt hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">MeasureSpec.EXACTLY <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//確定模式,父View希望子View的大小是確定的,由specSize決定;</span> MeasureSpec.AT_MOST <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//最多模式,父View希望子View的大小最多是specSize指定的值;</span> MeasureSpec.UNSPECIFIED <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//未指定模式,父View完全依據子View的設計值來決定; </span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul></strong>
-
View的measure方法是final的,不允許過載,View子類只能過載onMeasure來完成自己的測量邏輯。
-
最頂層DecorView測量時的MeasureSpec是由ViewRootImpl中getRootMeasureSpec方法確定的(LayoutParams寬高參數均為MATCH_PARENT,specMode是EXACTLY,specSize為物理螢幕大小)。
-
ViewGroup類提供了measureChild,measureChild和measureChildWithMargins方法,簡化了父子View的尺寸計算。
-
只要是ViewGroup的子類就必須要求LayoutParams繼承子MarginLayoutParams,否則無法使用layout_margin引數。
-
View的佈局大小由父View和子View共同決定。
-
使用View的getMeasuredWidth()和getMeasuredHeight()方法來獲取View測量的寬高,必須保證這兩個方法在onMeasure流程之後被呼叫才能返回有效值。
3 View繪製流程第二步:遞迴layout原始碼分析
在上面的背景介紹就說過,當ViewRootImpl的performTraversals中measure執行完成以後會接著執行mView.layout,具體如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">performTraversals</span>() { ...... mView.layout(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
可以看見layout方法接收四個引數,這四個引數分別代表相對Parent的左、上、右、下座標。而且還可以看見左上都為0,右下分別為上面剛剛測量的width和height。
至此又迴歸到View的layout(int l, int t, int r, int b)方法中去實現具體邏輯了,所以接下來我們開始分析View的layout過程。
整個View樹的layout遞迴流程圖如下:
3-1 layout原始碼分析
layout既然也是遞迴結構,那我們先看下ViewGroup的layout方法,如下:
<code class="language-java hljs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">layout</span>(<span class=
相關推薦
Android中view繪製過程
1 背景 看見沒有,如上圖中id為content的內容就是整個View樹的結構,所以對每個具體View物件的操作,其實就是個遞迴的實現。 前面《Android觸控式螢幕事件派發機制詳解與原始碼分析一(View篇)》文章的3-1小節說過And
android-進階(3)-自定義view(2)-Android中View繪製流程以及相關方法的分析
最近正在學自定義view,這篇文章主要講view的繪製流程和一些相關的方法,淺顯易懂,寫的非常好,忍不住就轉載了。 前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。
Android中View繪製流程以及invalidate()等相關方法分析
前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。同時真摯地向渴望瞭解Android 框架層的網友,推薦這本書,希望你們能夠在Android開發裡學到更多的知識 。 整個V
Android中View的繪製過程 onMeasure方法簡述 附有自定義View例子
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use thi
原始碼解析Android中View的measure量算過程
本文比較長,希望大家耐心讀完。 Android中的Veiw從記憶體中到呈現在UI介面上需要依次經歷三個階段:量算 -> 佈局 -> 繪圖,關於View的量算、佈局、繪圖的總體機制可參見博文《 Android中View的佈局及繪圖機制》。如果想了解l
原始碼解析Android中View的layout佈局過程
Android中的Veiw從記憶體中到呈現在UI介面上需要依次經歷三個階段:量算 -> 佈局 -> 繪圖,關於View的量算、佈局、繪圖的總體機制可參見博文 《 Android中View的佈局及繪圖機制》。量算是佈局的基礎,如果想了解量算的細節,可參
android 中View, Window, Activity, WindowManager,ViewRoot幾者之間的關系
line 消息傳遞 post att 顯示 增加 調用 eas window對象 (1)View:最主要的UI組件,表示屏幕上的一個矩形區域。 (2)Window: 表示一個窗體,不一定有屏幕那麽大,能夠非常大也能夠非常小;
Android中View滑動實現方式
必須 elf 內部 track 視圖 相對 top roc mar 滑動作為Android中最基礎的特效之一,使用場景非常廣泛。實現的方式也有多種,理解各種滑動的實現方式。清楚在開發中根據自己的實際需求,選擇合理的實現方案。這篇文章從:scrollTo()/scroll
Android中View的getX,getY...
都是 raw 知識 left 相對 就是 alt 就會 pos Android坐標系的理解直接關系到後面你Android進階部分的學習,如果沒有夯實的基礎,你自定義View時就會有種欲求不滿,欲罷不能——想搞懂卻又覺得難,想放棄又覺得舍不得。
Android中View的知識體系之基礎知識
[TOC] 網上有很多寫關於Android中view的文章,關於view 的知識比較多也比較雜,每次從網上找到相關文章後,都有新的收穫。這次準備寫關於view知識體系的文章,也吸取了網上許多優秀的文章的觀點和案例並加上自己的理解和感悟,這系列的文章會從view
view繪製過程筆記
view繪製過程 繪製過程由Viewroot的perfortraversals 開始依次呼叫performMeasure,performlayout,performondraw,performMeasure回撥用onmeasure方法,ommeasure方法會遍歷子view,依次呼叫me
Android中view的Left、Top、Right、Bottom
View:getLeft()、getTop()、getRight()、getBottom() 這幾個方法表示的具體座標值容易混淆,故現在記下來 看下圖佈局,相對佈局中含有ImageView,ImageView的getLeft()、getTop()、getRight()
Android中View事件分發機制
View事件分發機制 今天要寫一寫Android中比較重要的一個核心,View事件分發機制。那麼事件分發機制是什麼,為什麼要寫這個呢, 下面將一一講解出來。 前言 相信大家對Android基礎知識都已經有所瞭解啦,因為畢竟Android已經涼了,應該也沒有多少新
Android中Activity啟動過程探究
首先追溯到Activity的啟動,隨便啟動一個自己寫的demo專案,使用DDMS進行debug標記,然後在Debug中把主執行緒暫停,可以看到呼叫棧。如下圖所示: 於是我們先看android.app.ActivityThread的main()方法。 android.a
用Kotlin封裝一個Android中View的BackgroundDrawableBuilder
一個自定義的TextView,對background的設定進行了簡單封裝。同時包含了一個ShapeBuilder,可以用於設定給所有View新增背景 簡化View的background建立,支援在xml和程式碼中設定backgroundDrawable, 程式碼中支援鏈
Android中View自定義XML屬性詳解以及R.attr與R.styleable的區別
為View新增自定義XML屬性 Android中的各種Widget都提供了很多XML屬性,我們可以利用這些XML屬性在layout檔案中為Widget的屬性賦值。 如下所示: <TextView android:layout_wi
Android中View的彈性滑動——Android開發藝術探索筆記
介紹 彈性滑動也就是漸進式滑動,實現彈性滑動的方法有很多,但是他們都有一個共同的思想:將一次大的滑動分成若干次小的滑動並在一段時間內完成。本文主要介紹三種彈性滑動方式,Scroller、動畫和Handler。 本文中的“滑動”是指View內容的滑動而非V
Android中View面試相關
什麼是View? Android.app.View 就是手機的UI,View 負責繪製UI,處理事件(evnet),Android 利用 View 打造出所 Widgets,利用 Widget 可打造出互動式的使用者介面,每個View 負責一定區域的繪製。 View 座標
Android中APK安裝過程及原理解析
應用安裝是智慧機的主要特點,即使用者可以把各種應用(如遊戲等)安裝到手機上,並可以對其進行解除安裝等管理操作。APK是Android Package的縮寫,即Android安裝包。APK是類似Symbian Sis或Sisx的檔案格式。通過將APK檔案直接傳到Android模擬器或Android手機中執行即可
Android中JNI呼叫過程簡述
1.安裝和下載cygwin,下載Android NDK; 2.在ndk專案中JNI介面的設計; 3.使用C/C++實現本地方法; 4.JNI生成動態連結庫.so檔案; 5.將動態連結庫複製到java工程,在Java工程中呼叫,執行Java工程即可。