什么是自定義viewGroup呢?
??? 其實我們經(jīng)常會聽到自定義view但是并沒有聽到過自定義的viewgroup,其實自定的viewgroup是自定義view的一個細(xì)分,我們把自定義view和自定義viewgroup細(xì)分.
自定義view就是說在沒有現(xiàn)成的view。需要自己實現(xiàn)的時候,就使用自定義view,一般繼承自view,比如surfaceview或者其他的view
自定義viewgroup一般是利用現(xiàn)有的組件根據(jù)特定的布局方式來組成新的組件,大多繼承自viewgroup或各種layout
2. 自定義view的繪制流程

自己畫的圖很丑,將就著看把,其實我們可以看到我們必須要在測量之后再來布局這個viewgroup。然而在我們自定義viewgroup的時候其實最重要的時onmeasure和onlayout
在我們自定義view的時候最重要的時ondraw,其實我們在坐界面開發(fā)的過程中用得最多的時自定義viewgroup
3. View的層次結(jié)構(gòu)(view樹)

4. 開始
以flowlayout舉例,首先我們需要測量view的大小,現(xiàn)在我們首先要知道子view的大小和寬高才能確定整個flowlayout的寬高,所以我們需要先循環(huán)找到所有子view來確定flowlayout 的寬高所以我們就有
int childCount = getChildCount();for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);????
childView.measure(widthMeasureSpec,heightMeasureSpec);
}
但是現(xiàn)在我們并不知道子view的寬高,所以現(xiàn)在需要確定子view的寬高,但是現(xiàn)在一般我們會存在三種情況
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_width="100dp"
第三種我們可以很明確的知道子view的寬度,但是前面兩種我們并不知道,這時候我們需要用到layoutparmas,首先layoutparmas是什么呢?他是viewgroup的一個內(nèi)部類,他內(nèi)部實現(xiàn)了
public static final int MATCH_PARENT = -1;
public static final int WRAP_CONTENT = -2;
public int width;
也就是說它本身就代表了我們這三種情況,所以就有
LayoutParams layoutParams = childView.getLayoutParams();
layoutParams.width;
layoutParams.height;
我們就能得到這個子view的寬高,但是我們并不知道具體的寬高,然后我們在轉(zhuǎn)換的時候還需要用到measurespec,那measurespec又是什么呢?它是view的一個內(nèi)部類,它內(nèi)部實現(xiàn)了三種mode(狀態(tài)),UNSPECIFIED:不對view大小進行限制,EXACTLY:確切的大小,如100dp,AT_MOST:大小不可超過某數(shù)值,如:match_parent,最大不能超過父布局,在它內(nèi)部以二進制的運算來得出結(jié)果,由于int為三十二位,用高兩位來表述mode,低三十位來表示size,MODE_SHIFT=30的作用是移位
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY ????= 1 << MODE_SHIFT;
public static final int AT_MOST ????= 2 << MODE_SHIFT;
private static final int MODE_SHIFT = 30;
然后我們就能得到measurespec的創(chuàng)建規(guī)則

然后在這個算法上我們就能找到getChildMeasureSpec這個方法,我們來看下
case MeasureSpec.EXACTLY:
if (childDimension >=0) {
resultSize = childDimension;
? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? }else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
? ? ? ? resultSize = size;
? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? }else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
? ? ? ? resultSize = size;
? ? ? ? resultMode = MeasureSpec.AT_MOST;
? ? }
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >=0) {
// Child wants a specific size... so be it
? ? ? ? resultSize = childDimension;
? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? }else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
? ? ? ? resultSize = size;
? ? ? ? resultMode = MeasureSpec.AT_MOST;
? ? }else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
? ? ? ? resultSize = size;
? ? ? ? resultMode = MeasureSpec.AT_MOST;
? ? }
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >=0) {
// Child wants a specific size... let him have it
? ? ? ? resultSize = childDimension;
? ? ? ? resultMode = MeasureSpec.EXACTLY;
? ? }else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
? ? ? ? resultSize = View.sUseZeroUnspecifiedMeasureSpec ?0 : size;
? ? ? ? resultMode = MeasureSpec.UNSPECIFIED;
? ? }else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
? ? ? ? resultSize = View.sUseZeroUnspecifiedMeasureSpec ?0 : size;
? ? ? ? resultMode = MeasureSpec.UNSPECIFIED;
? ? }
break;
我們可以看到創(chuàng)建的規(guī)則從而得到我們相應(yīng)的數(shù)值,所以我們可以得到以下代碼
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,paddingLeft + paddingRight,childLayoutParams.width);int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,paddingLeft + paddingRight,childLayoutParams.height);
我們來看看這個方法和參數(shù)
public static int getChildMeasureSpec(int spec, int padding, int childDimension)
第一個是父view的spec,第三個是子view的dimension,但是第二個參數(shù)會存疑,并不能確定到底是誰的padding,在這里其實是父類的padding,因為在我們計算子view的measure的時候其實最大是不能超過含有padding距離的父view的,所以這里傳的是父view的padding那現(xiàn)在我們就能得到子view的measure
childView.measure(childWidthMeasureSpec,childHeightMeasureSpec);
接下來我們需要測量本view的寬高,但是會存在,當(dāng)我們流式布局不固定,所以我們應(yīng)該取某一行最長的作寬,所有行加起來作高,所以現(xiàn)在我們需要將一行所有的view和行高還有這行已經(jīng)使用的size進行記錄
List<View> lineViews = new ArrayList<>();
int lineWidthUsed = 0;
int lineHeight = 0;
lineViews.add(childView);
lineWidthUsed = lineWidthUsed + childMeasureWidth + mHorizonalSpecing;lineHeight = Math.max(lineHeight,childMeasureHeight);
但是當(dāng)我們已經(jīng)使用的超過了一行的寬度,那么我們就需要
if (childMeasureWidth + lineWidthUsed + mHorizonalSpecing > selfWidth){????
parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpecing;???
?parentNeededWidth = Math.max(parentNeededWidth,lineWidthUsed + mHorizonalSpecing);????
lineViews = new ArrayList<>();???
?lineWidthUsed = 0;????
lineHeight = 0;
}
那最后我們就得到
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int realWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : parentNeededWidth;
int realHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : parentNeededHeight;
setMeasuredDimension(realWidth,realHeight);
那么我們接下來應(yīng)該加載布局
int lineCount = allLines.size();
int curLeft = getPaddingLeft();
int curTop = getPaddingTop();
for (int i = 0; i < lineCount; i++) {
List<View> lineView = allLines.get(i);
int lineHeight = lineHeights.get(i);
for (int i1 = 0; i1 < lineView.size(); i1++) {
View view = lineView.get(i1);
int right = curLeft + view.getMeasuredWidth();
int bottom = curTop + view.getMeasuredHeight();????????
view.layout(curLeft, curTop,right,bottom);???????
?curLeft = right + mHorizontalSpacing;???
}
curLeft = getPaddingLeft();???
curTop = curTop + lineHeight + mVerticalSpacing;
}