日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java linearlayout_LinearLayout属性用法和源码分析

發(fā)布時間:2023/12/10 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java linearlayout_LinearLayout属性用法和源码分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

對于一個View(ViewGroup)來說實現(xiàn)無非于三個流程,onMeasure(測量),onLayout(定位),onDraw(繪制),接下來就對這三個部分一一分析

但是首先還是對LinearLayout變量進行介紹

1.LinearLayout變量

其實LinearLayout變量與上篇屬性篇中關(guān)聯(lián)比較大,這里就直接上代碼和注釋了

//基準線對齊變量,默認為true

private boolean mBaselineAligned = true;

//基準線對齊的對象index

private int mBaselineAlignedChildIndex = -1;

//baseline額外的偏移量

private int mBaselineChildTop = 0;

//linearlayout的排列方式

private int mOrientation;

//linearlayout的對齊方式

private int mGravity = Gravity.START | Gravity.TOP;

//測量的時候通過累加得到所有子控件的高度和(Vertical)或者寬度和(Horizontal) ;

private int mTotalLength;

//權(quán)重總和變量

private float mWeightSum;

//權(quán)重最小尺寸的對象

private boolean mUseLargestChild;

//基準線對其相關(guān)

private int[] mMaxAscent;

private int[] mMaxDescent;

//分隔條相關(guān)

private Drawable mDivider;

private int mDividerWidth;

private int mDividerHeight;

private int mShowDividers;

private int mDividerPadding;

2.measure流程

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if (mOrientation == VERTICAL) {

measureVertical(widthMeasureSpec, heightMeasureSpec);

} else {

measureHorizontal(widthMeasureSpec, heightMeasureSpec);

}

}

當我們設(shè)置不同的orientation就會進入不同的測量流程,我們以其中的一個測量流程為例子進行說明,那么另外的測量流程也就不難理解了

我們以measureVertical為例來分析

由于代碼相對比較長,所以根據(jù)不同的功能分段分析

(1).變量

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {

//mTotalLength為 LinearLayout的成員變量,在這里指的是所有子控件的高度和

mTotalLength = 0;

//所有子控件中寬度最大的值

int maxWidth = 0;

//子控件的測量狀態(tài)

int childState = 0;

//子控件中l(wèi)ayout_weight<=0的View最大高度

int alternativeMaxWidth = 0;

//子控件中l(wèi)ayout_weight>0的View最大高度

int weightedMaxWidth = 0;

//子控件是否全是match_parent

boolean allFillParent = true;

//子控件所有l(wèi)ayout_weight的和

float totalWeight = 0;

//獲取子控件數(shù)量

final int count = getVirtualChildCount();

//獲取測量模式

final int widthMode = MeasureSpec.getMode(widthMeasureSpec);

final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

//當子控件為match_parent的時候,該值為ture

boolean matchWidth = false;

boolean skippedMeasure = false;

//基準線對齊的對象index

final int baselineChildIndex = mBaselineAlignedChildIndex;

//權(quán)重最小尺寸的對象

final boolean useLargestChild = mUseLargestChild;

//子View中最高高度

int largestChildHeight = Integer.MIN_VALUE;

}

(2).測量Part1

其實這段代碼前面自帶的注釋就很好說明接下來這一段代碼做的事情了,

See how tall everyone is. Also remember max width.

就不對這句話進行翻譯了,接下來看這一段代碼做了什么事情

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {

//...接上面的變量

for (int i = 0; i < count; ++i) {

final View child = getVirtualChildAt(i);

//這個不解釋了,measureNullChild獲得的結(jié)果是0

if (child == null) {

mTotalLength += measureNullChild(i);

continue;

}

//這個也不解釋了

if (child.getVisibility() == View.GONE) {

i += getChildrenSkipCount(child, i);

continue;

}

// 根據(jù)showDivider的值(before/middle/end)來決定遍歷到當前子控件時,高度是否需要加上divider的高度

// 比如showDivider為before,那么只會在第0個子控件測量時加上divider高度,其余情況下都不加

//這里測量不包括end的情況

if (hasDividerBeforeChildAt(i)) {

mTotalLength += mDividerHeight;

}

LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

//根據(jù)子控件的權(quán)重得到總權(quán)重

totalWeight += lp.weight;

// 測量模式有三種:

// * UNSPECIFIED:父控件對子控件無約束

// * Exactly:父控件對子控件強約束,子控件永遠在父控件邊界內(nèi),越界則裁剪。如果要記憶的話,可以記憶為有對應(yīng)的具體數(shù)值或者是Match_parent

// * AT_Most:子控件為wrap_content的時候,測量值為AT_MOST。

if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {

//父控件高度為match_parent且子控件高度為0,weight>0情況下

// 測量到這里的時候,會給個標志位,稍后再處理。此時會計算總高度

final int totalLength = mTotalLength;

mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);

skippedMeasure = true;

} else {

int oldHeight = Integer.MIN_VALUE;

if (lp.height == 0 && lp.weight > 0) {

//子控件高度為0并且weight>0,并且父控件是wrap_content,或者mode為UNSPECIFIED

//這時候父控件的高度是wrap_content,所以隨著子控件的高度進行變化的

//顧強制將子控件高度設(shè)置為wrap_content,防止子控件高度為0

oldHeight = 0;

lp.height = LayoutParams.WRAP_CONTENT;

}

//方法名可知是對子控件進行測量

measureChildBeforeLayout(

child, i, widthMeasureSpec, 0, heightMeasureSpec,

totalWeight == 0 ? mTotalLength : 0);

if (oldHeight != Integer.MIN_VALUE) {

lp.height = oldHeight;

}

final int childHeight = child.getMeasuredHeight();

final int totalLength = mTotalLength;

//比較child測量前后的總高度,取大值

mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +

lp.bottomMargin + getNextLocationOffset(child));

//當設(shè)置權(quán)重最小尺寸的對象為true,獲取子View中最高高度

if (useLargestChild) {

largestChildHeight = Math.max(childHeight, largestChildHeight);

}

}

//計算baseline額外的偏移量,后面會用到

if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {

mBaselineChildTop = mTotalLength;

}

//當設(shè)置的基準線對齊的對象index 大于 子對象的Index 并且 weight > 0 會報異常

if (i < baselineChildIndex && lp.weight > 0) {

throw new RuntimeException("A child of LinearLayout with index "

+ "less than mBaselineAlignedChildIndex has weight > 0, which "

+ "won't work. Either remove the weight, or don't set "

+ "mBaselineAlignedChildIndex.");

}

// 當父類(LinearLayout)不是match_parent或者精確值的時候,但子控件卻是一個match_parent

// 那么matchWidthLocally和matchWidth置為true

// 意味著這個控件將會占據(jù)父類(水平方向)的所有空間

boolean matchWidthLocally = false;

if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {

matchWidth = true;

matchWidthLocally = true;

}

final int margin = lp.leftMargin + lp.rightMargin;

final int measuredWidth = child.getMeasuredWidth() + margin;

//后面幾個就是給變量賦值

maxWidth = Math.max(maxWidth, measuredWidth);

childState = combineMeasuredStates(childState, child.getMeasuredState());

allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

if (lp.weight > 0) {

weightedMaxWidth = Math.max(weightedMaxWidth,

matchWidthLocally ? margin : measuredWidth);

} else {

alternativeMaxWidth = Math.max(alternativeMaxWidth,

matchWidthLocally ? margin : measuredWidth);

}

i += getChildrenSkipCount(child, i);

}

}

(3).測量Part2

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {

//...接上面的方法

//判斷showDivider值是否為end,是的情況下加上divider的高度

if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {

mTotalLength += mDividerHeight;

}

if (useLargestChild &&

(heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {

//當設(shè)置權(quán)重最小尺寸的對象為true

//并且LinearLayout是wrap_content,或者mode為UNSPECIFIED

//計算新的mTotalLength,因為這時候所有子控件都是用最大控件的最小值

mTotalLength = 0;

for (int i = 0; i < count; ++i) {

final View child = getVirtualChildAt(i);

if (child == null) {

mTotalLength += measureNullChild(i);

continue;

}

if (child.getVisibility() == GONE) {

i += getChildrenSkipCount(child, i);

continue;

}

final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)

child.getLayoutParams();

final int totalLength = mTotalLength;

mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +

lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));

}

}

//下面是計算屏幕除去所有子控件所占高度剩余的高度

//為了定義權(quán)重的子控件計算高度

mTotalLength += mPaddingTop + mPaddingBottom;

int heightSize = mTotalLength;

heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);

heightSize = heightSizeAndState & MEASURED_SIZE_MASK;

int delta = heightSize - mTotalLength;

}

(3).測量Part3

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {

//...接上面的方法

if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {

//這里skippedMeasure是接的上面測量Part1,當父控件為match_parent,子控件height =0 ,weight>0的情況下skippedMeasure為true

//這里獲取總權(quán)重,當我們設(shè)置了總權(quán)重則用我們設(shè)置的權(quán)重值,如果沒有設(shè)置,則用子控件權(quán)重相加的和

float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;

mTotalLength = 0;

for (int i = 0; i < count; ++i) {

//遍歷子View,根據(jù)權(quán)重對子View進行測量

final View child = getVirtualChildAt(i);

if (child.getVisibility() == View.GONE) {

continue;

}

LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

float childExtra = lp.weight;

if (childExtra > 0) {

//當子控件的weight大于0時

int share = (int) (childExtra * delta / weightSum);

weightSum -= childExtra;

delta -= share;

final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,

mPaddingLeft + mPaddingRight +

lp.leftMargin + lp.rightMargin, lp.width);

if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {

int childHeight = child.getMeasuredHeight() + share;

if (childHeight < 0) {

childHeight = 0;

}

//定義權(quán)重子控件重新測量,這時候childWidth是子控件本身的高度加上通過權(quán)重計算的額外高度

child.measure(childWidthMeasureSpec,

MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));

} else {

//沒有定義權(quán)重子控件重新測量,當額外高度大于0,則以這個額外高度為子控件的高度

child.measure(childWidthMeasureSpec,

MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,

MeasureSpec.EXACTLY));

}

childState = combineMeasuredStates(childState, child.getMeasuredState()

& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));

}

final int margin = lp.leftMargin + lp.rightMargin;

final int measuredWidth = child.getMeasuredWidth() + margin;

maxWidth = Math.max(maxWidth, measuredWidth);

boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&

lp.width == LayoutParams.MATCH_PARENT;

alternativeMaxWidth = Math.max(alternativeMaxWidth,

matchWidthLocally ? margin : measuredWidth);

allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

final int totalLength = mTotalLength;

mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +

lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));

}

mTotalLength += mPaddingTop + mPaddingBottom;

} else {

alternativeMaxWidth = Math.max(alternativeMaxWidth,

weightedMaxWidth);

//當設(shè)置了權(quán)重最小尺寸

if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {

for (int i = 0; i < count; i++) {

final View child = getVirtualChildAt(i);

if (child == null || child.getVisibility() == View.GONE) {

continue;

}

final LinearLayout.LayoutParams lp =

(LinearLayout.LayoutParams) child.getLayoutParams();

float childExtra = lp.weight;

//子控件設(shè)置權(quán)重后,就會以最大子元素的最小尺寸作為高度

if (childExtra > 0) {

child.measure(

MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),

MeasureSpec.EXACTLY),

MeasureSpec.makeMeasureSpec(largestChildHeight,

MeasureSpec.EXACTLY));

}

}

}

}

if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {

maxWidth = alternativeMaxWidth;

}

maxWidth += mPaddingLeft + mPaddingRight;

maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),

heightSizeAndState);

if (matchWidth) {

forceUniformWidth(count, heightMeasureSpec);

}

}

到這里測量Vertical就結(jié)束了,接下來介紹下Layout的過程

2.layout流程

與measure一樣layout同樣是分兩個流程

protected void onLayout(boolean changed, int l, int t, int r, int b) {

if (mOrientation == VERTICAL) {

layoutVertical(l, t, r, b);

} else {

layoutHorizontal(l, t, r, b);

}

}

同樣我們?nèi)ayoutVertical來進行分析,layoutVertical相對代碼不是很多,就不拆分分析了

void layoutVertical(int left, int top, int right, int bottom) {

final int paddingLeft = mPaddingLeft;

int childTop;

int childLeft;

final int width = right - left;

int childRight = width - mPaddingRight;

//子控件可用的空間

int childSpace = width - paddingLeft - mPaddingRight;

final int count = getVirtualChildCount();

final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;

final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;

//根據(jù)LinearLayout的對其方式,設(shè)置第一個子控件的Top值

switch (majorGravity) {

case Gravity.BOTTOM:

childTop = mPaddingTop + bottom - top - mTotalLength;

break;

case Gravity.CENTER_VERTICAL:

childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;

break;

case Gravity.TOP:

default:

childTop = mPaddingTop;

break;

}

for (int i = 0; i < count; i++) {

final View child = getVirtualChildAt(i);

if (child == null) {

childTop += measureNullChild(i);

} else if (child.getVisibility() != GONE) {

final int childWidth = child.getMeasuredWidth();

final int childHeight = child.getMeasuredHeight();

final LinearLayout.LayoutParams lp =

(LinearLayout.LayoutParams) child.getLayoutParams();

int gravity = lp.gravity;

if (gravity < 0) {

gravity = minorGravity;

}

final int layoutDirection = getLayoutDirection();

final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);

//根據(jù)子控件的對其方式設(shè)置left值

switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {

case Gravity.CENTER_HORIZONTAL:

childLeft = paddingLeft + ((childSpace - childWidth) / 2)

+ lp.leftMargin - lp.rightMargin;

break;

case Gravity.RIGHT:

childLeft = childRight - childWidth - lp.rightMargin;

break;

case Gravity.LEFT:

default:

childLeft = paddingLeft + lp.leftMargin;

break;

}

//當有設(shè)置分隔條,需要加上分隔條的高度

if (hasDividerBeforeChildAt(i)) {

childTop += mDividerHeight;

}

//子控件top遞增

childTop += lp.topMargin;

//用setChildFrame()方法設(shè)置子控件控件的在父控件上的坐標軸

setChildFrame(child, childLeft, childTop + getLocationOffset(child),

childWidth, childHeight);

childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);

i += getChildrenSkipCount(child, i);

}

}

}

3.draw流程

最后說下draw流程,draw流程相對來說沒有什么內(nèi)容,

protected void onDraw(Canvas canvas) {

if (mDivider == null) {

return;

}

if (mOrientation == VERTICAL) {

drawDividersVertical(canvas);

} else {

drawDividersHorizontal(canvas);

}

}

measure和layout將子控件的位置和大小確定后當有設(shè)置分隔條則分為兩種流程繪制分隔條

void drawDividersVertical(Canvas canvas) {

final int count = getVirtualChildCount();

//當分割線位置設(shè)置begin與middle走下面流程

for (int i = 0; i < count; i++) {

final View child = getVirtualChildAt(i);

if (child != null && child.getVisibility() != GONE) {

if (hasDividerBeforeChildAt(i)) {

final LayoutParams lp = (LayoutParams) child.getLayoutParams();

final int top = child.getTop() - lp.topMargin - mDividerHeight;

drawHorizontalDivider(canvas, top);

}

}

}

//當分割線位置設(shè)置end走下面流程

if (hasDividerBeforeChildAt(count)) {

final View child = getLastNonGoneChild();

int bottom = 0;

if (child == null) {

bottom = getHeight() - getPaddingBottom() - mDividerHeight;

} else {

final LayoutParams lp = (LayoutParams) child.getLayoutParams();

bottom = child.getBottom() + lp.bottomMargin;

}

drawHorizontalDivider(canvas, bottom);

}

}

至此,LinearLayout的核心代碼就分析完成了

總結(jié)

以上是生活随笔為你收集整理的java linearlayout_LinearLayout属性用法和源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。