measureChildren作品
無論是在改寫View依然是ViewGroup什么時候。特別ViewGrop什么時候,通常是不可避免的重寫onMeasure方法,我們一定會調(diào)用setMeasuredDimension()將測量好的寬高值傳遞進(jìn)去。也不免調(diào)用measureChildren方法。來測量全部的子View的大小,以下我們看看measureChildren方法是怎樣工作的。
這對我們重寫onMeasure無疑是非常有幫助的。由于一般我們都會看到這一行代碼
可是它究竟測量到什么程度,滿足不滿足我們自己定義ViewGroup對以下一系列child尺寸的測量需求,不知道這個我們寫代碼就心里沒底。
所以我們有必要扒出它的老底來看看。由此來決定我們能否夠直接使用這種方法,還是因為我們有很多其它的效果要實現(xiàn),有很多其它的因素須要考慮。這種方法不能滿足需求,須要自己寫方法來測量child。
同一時候我們在有必要又一次寫方法來測量child的時候,我們也要從自帶方法的思路開始擴(kuò)展。
說了一大堆。總之這個問題非常重要。
以下要了解它的工作原理,我們還是要來看看源代碼:
(一)首先是measureChildren
?/**?* 遍歷全部的子view去測量自己(跳過GONE類型View)?* @param widthMeasureSpec 從父容器傳遞給子容器的布局需求(寬)* @param heightMeasureSpec 從父容器傳遞給子容器的布局需求(高)*/ ? protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } }
這部分非常easy,也就是遍歷全部的子View,假設(shè)View的狀態(tài)不是GONE就調(diào)用measureChild去進(jìn)行下一步的測量。
(二)所以我們再來看一下measureChild
/** * 測量單個視圖。將寬高和padding加在一起后交給getChildMeasureSpec去獲得終于的測量值 * @param child 須要測量的子視圖 <pre name="code" class="java">?* @param widthMeasureSpec 從父容器傳遞給子容器的布局需求(寬)* @param heightMeasureSpec 從父容器傳遞給子容器的布局需求(高) */ protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { // 取得子視圖的布局參數(shù) final LayoutParams lp = child.getLayoutParams(); // 通過getChildMeasureSpec獲取終于的寬高具體測量值 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); // 將計算好的寬高具體測量值傳入measure方法。完畢最后的測量 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
這種方法就是對一個子視圖進(jìn)行測量。當(dāng)中一個重要的方法就是getChildMeasureSpec()。
(三)所以我們再來看一下getChildMeasureSpec
/** * * 結(jié)合父view的MeasureSpec與子view的LayoutParams信息去找到最好的結(jié)果 * (子view的確切大小由雙方面共同決定:父view的MeasureSpec 子view的LayoutParams屬性) * * @param spec 父view的MeasureSpec* @param padding view當(dāng)前尺寸的的內(nèi)邊距和外邊距(padding,margin) * @param childDimension child在當(dāng)前尺寸下的布局參數(shù)寬高值(LayoutParam.width,height) */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { //父view的模式和大小 int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); //通過父view計算出的子view = 父大小-邊距(父要求的大小。但子view不一定用這個值) int size = Math.max(0, specSize - padding); //子view想要的實際大小和模式(須要計算) int resultSize = 0; int resultMode = 0; //通過1.父view的MeasureSpec 2.子view的LayoutParams屬性這兩點來確定子view的大小 switch (specMode) { // 當(dāng)父view的模式為EXACITY時,父view強(qiáng)加給子view確切的值 case MeasureSpec.EXACTLY: // 當(dāng)子view的LayoutParams>0也就是有確切的值 if (childDimension >= 0) { //子view大小為子自身所賦的值,模式大小為EXACTLY resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; // 當(dāng)子view的LayoutParams為MATCH_PARENT時(-1) } else if (childDimension == LayoutParams.MATCH_PARENT) { //子view大小為父view大小,模式為EXACTLY resultSize = size; resultMode = MeasureSpec.EXACTLY; // 當(dāng)子view的LayoutParams為WRAP_CONTENT時(-2) } else if (childDimension == LayoutParams.WRAP_CONTENT) { //子view決定自己的大小。但最大不能超過父view,模式為AT_MOST resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // 當(dāng)父view的模式為AT_MOST時,父view強(qiáng)加給子view一個最大的值。 case MeasureSpec.AT_MOST: // 道理同上 if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // 當(dāng)父view的模式為UNSPECIFIED時,子view為想要的值 case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // 子view大小為子自身所賦的值 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // 由于父view為UNSPECIFIED,所以MATCH_PARENT的話子類大小為0 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // 由于父view為UNSPECIFIED,所以WRAP_CONTENT的話子類大小為0 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
總而言之,這些推斷和設(shè)置事實上就是依據(jù)三種模式以及傳入的尺寸要求,還有須要考慮的padding和margin之后。比較全面的計算出了一個測量值,了解了這些之后我們就能夠確定什么時候須要自己寫關(guān)于子視圖的測量部分。什么時候我們僅僅須要簡單的一行代碼:
// 計算出全部的childView的寬和高 measureChildren(widthMeasureSpec, heightMeasureSpec);
就能夠滿足我們的需求了,所以一切還是按需來處理。
在我個人看來,這種方法考慮的比我最初想象的要全面多了,看來除了有比較特殊的需求,大部分的時候都是能夠直接使用這種方法的。這還是省了不少事的。
假設(shè)您對我提到的模式或者是重寫過程不大了解的,詳細(xì)的關(guān)于重寫onMeasure內(nèi)容請詳見我的另外一篇博客:
http://blog.csdn.net/sunmc1204953974/article/details/38454267
希望大家能有所收獲,我也是學(xué)生。有什么寫的不好的地方還請多多不吝賜教!
版權(quán)聲明:本文博客原創(chuàng)文章,博客,未經(jīng)同意,不得轉(zhuǎn)載。
轉(zhuǎn)載于:https://www.cnblogs.com/zfyouxi/p/4634324.html
總結(jié)
以上是生活随笔為你收集整理的measureChildren作品的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nginx + PHP CGI的fix_
- 下一篇: C#实现bitmap图像矫正