淺談自定義ViewGroup

什么是自定義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;

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容