android view layout原始碼分析
view.java
/**
* Assign a size and position to a view and all of its
* descendants
*
* <p>This is the second phase of the layout mechanism.
* (The first is measuring). In this phase, each parent calls
* layout on all of its children to position them.
* This is typically done using the child measurements
* that were stored in the measure pass().</p>
*
* <p>Derived classes should not override this method.
* Derived classes with children should override
* onLayout. In that method, they should
* call layout on each of their children.</p>
*
* @param l Left position, relative to parent
* @param t Top position, relative to parent
* @param r Right position, relative to parent
* @param b Bottom position, relative to parent
*/
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
/**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
view的layout方法是可以重寫的
儲存原資料(setFrame中儲存新資料)
changed 分析
處理新增的OnLayoutChangeListener (view.java的addOnLayoutChangeListener)
boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);分析
//view.java
/**
* Return true if o is a ViewGroup that is laying out using optical bounds.
* @hide
*/
public static boolean isLayoutModeOptical(Object o) {
return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical();
}
//viewgroup.java
/** Return true if this ViewGroup is laying out using optical bounds. */
boolean isLayoutModeOptical() {
return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
}
//view.java
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
Insets parentInsets = mParent instanceof View ?
((View) mParent).getOpticalInsets() : Insets.NONE;
Insets childInsets = getOpticalInsets();
return setFrame(
left + parentInsets.left - childInsets.left,
top + parentInsets.top - childInsets.top,
right + parentInsets.left + childInsets.right,
bottom + parentInsets.top + childInsets.bottom);
}
/**
* Assign a size and position to this view.
*
* This is called from layout.
*
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
* @return true if the new size and position are different than the previous ones 如果新的尺寸與原尺寸不相同返回真
* false時會繪製view
* {@hide}
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
if (mDisplayList != null) {
mDisplayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
}
mPrivateFlags |= PFLAG_HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= PFLAG_DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
if (mOverlay != null) {
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* @param w Current width of this view.
* @param h Current height of this view.
* @param oldw Old width of this view.
* @param oldh Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
}
setFrame 如果新的尺寸(寬高)與原尺寸不相同返回真會繪製view ,並呼叫自身的onSizeChanged
viewGroup.java
/**
* {@inheritDoc}
*/
@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
/**
* {@inheritDoc}
*/
@Override
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
此處的super.layout(l, t, r, b);呼叫的是父類view的layout,方法,在layout方法中呼叫viewgrooup的onlayout(抽象方法, 自定義viewgroup要重寫的),
windownPhone$decorView extends FrameLayout所以layout的起點是viewgroup的layout
Linearlayout.java
@Override
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);
}
}
/**
* Position the children during a layout pass if the orientation of this
* LinearLayout is set to {@link #VERTICAL}.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
* @param left
* @param top
* @param right
* @param bottom
*/
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// Where right end of child should go
final int width = right - left;
int childRight = width - mPaddingRight;
// Space available for child
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;
switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already
childTop = mPaddingTop + bottom - top - mTotalLength;
break;
// mTotalLength contains the padding already
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);
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;
}
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}
childTop += lp.topMargin;
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
i += getChildrenSkipCount(child, i);
}
}
}
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
setChildFrame 呼叫子元素的layout方法,
child.getMeasuredWidth() 返回的是onmeasure中要呼叫的 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)中設定的mMeasuredWidth(元素能顯示的寬度),
相關文章
- Android View 事件分發原始碼分析AndroidView事件原始碼
- Android View繪製原始碼分析 MeasureAndroidView原始碼
- Android原始碼分析之View繪製流程Android原始碼View
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- Weex-iOS原始碼-CSS Layout 分析iOS原始碼CSS
- Android系統原始碼分析--View繪製流程之-inflateAndroid原始碼View
- Android系統原始碼分析–View繪製流程之-setContentViewAndroid原始碼View
- Android系統原始碼分析--View繪製流程之-setContentViewAndroid原始碼View
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- Android View 原始碼解析(一) - setContentViewAndroidView原始碼
- Android面試複習之View事件體系(原始碼分析)Android面試View事件原始碼
- View繪製流程原始碼分析View原始碼
- Element原始碼分析系列1一Layout(佈局)原始碼
- Android View 原始碼解析(三) – View的繪製過程AndroidView原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- Android原始碼完全解析——View的Measure過程Android原始碼View
- Android Choreographer 原始碼分析Android原始碼
- Android 事件分發機制原始碼解析-view層Android事件原始碼View
- Android中IntentService原始碼分析AndroidIntent原始碼
- android IO Prefetch原始碼分析Android原始碼
- Android開源原始碼分析Android原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- Android從原始碼角度剖析View事件分發機制Android原始碼View事件
- Android自定義View之(一)View繪製流程詳解——向原始碼要答案AndroidView原始碼
- Android原始碼分析–ArrayMap優化Android原始碼優化
- Android Sensor原始碼分析總結Android原始碼
- Android 8.0 原始碼分析 (八) ActivityManagerServiceAndroid原始碼
- Android Jetpack系列——ViewModel原始碼分析AndroidJetpackView原始碼
- Android 系統原始碼-1:Android 系統啟動流程原始碼分析Android原始碼
- React Native 0.55.4 Android 原始碼分析(Java層原始碼解析)React NativeAndroid原始碼Java
- Android Activity啟動流程原始碼分析Android原始碼
- Android系統原始碼分析之-ContentProviderAndroid原始碼IDE
- Android 原始碼分析(二)handler 機制Android原始碼
- Android系統原始碼分析-事件收集Android原始碼事件
- Android 7 原始碼分析系列導讀Android原始碼
- Android原始碼分析:Activity啟動流程Android原始碼
- 直播平臺原始碼,Android自定義View實現呼吸燈效果原始碼AndroidView
- Android原始碼分析(LayoutInflater.from(this).inflate(resId,null);原始碼解析)Android原始碼Null