Android 보기 의 그리 기 프로 세 스(위)View 측정
View 의 그리 기 프로 세 스 는 measure,layot,draw 프로 세 스 로 나 눌 수 있 습 니 다.measure 는 View 의 측정 과정 을 나타 내 고 View 의 너비 와 높이 를 측정 하 는 데 사용 합 니 다.layot 는 View 가 부모 용기 의 위 치 를 확인 하 는 데 사 용 됩 니 다.draw 는 View 를 화면 에 그립 니 다.다음은 View 의 Measure 과정 을 살 펴 보 겠 습 니 다.
측정 과정
View 의 그리 기 프로 세 스 는 ViewRoot 의 permTraversals 방법 에서 시 작 된 것 으로 ViewRoot 는 ViewRootImpl 류 에 대응 합 니 다.ViewRoot 는 performTraversals 에서 performMeasure 방법 을 사용 하여 루트 View 를 측정 합 니 다.performance Measure 방법 에 서 는 View 의 measure 방법 을 호출 합 니 다.View 의 measure 방법 에 대해 서 는 final 유형 입 니 다.즉,이 measure 방법 은 이불 류 에 다시 쓸 수 없습니다.하지만 measure 방법 에서 onMeasure 방법 을 호출 했다.그래서 View 의 하위 클래스 는 onMeasure 방법 을 다시 써 서 각자 의 Measure 과정 을 실현 할 수 있다.여기 서 는 주로 onMeasure 방법 을 분석 하 는 것 이다.
MeasureSpec
MeasureSpec 는 View 클래스 의 정적 내부 클래스 입 니 다.하나의 MeasureSpec 는 부모 레이아웃 이 하위 레이아웃 에 전달 하 는 레이아웃 요 구 를 패키지 합 니 다.모든 MeasureSpec 은 높이 나 너비 의 요 구 를 대표 합 니 다.모든 MesureSpec 는 specSize 와 specMode 로 구성 되 어 있 으 며 32 비트 의 int 값 을 대표 합 니 다.그 중에서 높 은 2 비트 는 specSize 를 대표 하고 낮은 30 비트 는 specMode 를 대표 합 니 다.
Measure Spec 의 측정 모델 은 세 가지 가 있 습 니 다.다음은 이 세 가지 측정 모델 을 소개 합 니 다.
UNSPECIFIED
부모 용 기 는 하위 View 에 대해 아무런 제한 이 없습니다.하위 View 는 모든 크기 일 수 있 습 니 다.
EXACTLY
부모 용 기 는 하위 View 크기 로 구체 적 인 값 을 지정 합 니 다.View 의 최종 크기 는 specSize 입 니 다.대응 View 속성 matchparent 와 구체 적 인 값.
AT_MOST
하위 뷰 의 크기 는 최대 specSize,즉 하위 뷰 의 크기 는 specSize 를 초과 할 수 없습니다.View 속성 에 대응 하 는 wrapcontent.
MeasureSpec 에 서 는 specSize 와 specMode 를 통 해 MakeMeasureSpec 방법 으로 MeasureSpec 을 만 들 수 있 으 며,getMode 와 getSize 를 통 해 MeasureSpec 의 specMode 와 specSize 를 얻 을 수 있다.
View 의 측정 과정
위 에서 언급 한 바 와 같이 View 의 Measure 과정 은 measure 방법 으로 이 루어 지고 measure 방법 은 onMeasure 방법 을 호출 하여 View 의 Measure 과정 을 완성 한다.그럼 onMeasure 방법 을 살 펴 보 겠 습 니 다.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
View 의 onMeasure 방법 에 서 는 set Measured Dimension 방법 만 호출 되 었 을 뿐,set Measured Dimension 방법 은 View 의 높 고 넓 은 측정 치 를 설정 하 는 역할 을 합 니 다.View 측정 후 너비 와 높 은 값 은 getDefaultSize 방법 으로 얻 을 수 있 습 니 다.다음은 이 getDefault Size 방법 입 니 다.
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
MeasureSpec 에 대한 ATMOST 와 EXACTLY 모드 에서 바로 되 돌아 오 는 것 은 MeasureSpec 의 specSize 입 니 다.즉,이 specSize 는 View 가 측정 한 크기 입 니 다.UNSPECIFIED 모드 에서 View 의 측정 값 은 getDefaultSize 방법의 첫 번 째 매개 변수 size 입 니 다.이 size 에 대응 하 는 너비 와 높이 는 getSuggested Minimum Width 와 getSuggested Minimum Height 두 가지 방법 으로 얻 을 수 있 습 니 다.다음은 이 두 가지 방법 을 살 펴 보 겠 습 니 다.
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
여기 서 View 너비 와 높 은 수치 가 View 에 존재 하 는 배경 에 따라 설정 되 어 있 는 지 볼 수 있 습 니 다.여기 서 View 의 너비 로 설명 하 겠 습 니 다.View 에 배경 이 없 으 면 View 의 너비 인 mMinWidth 입 니 다.mMinWidth 는 XML 레이아웃 파일 에 minWidth 속성 을 설정 할 수 있 습 니 다.기본 값 은 0 입 니 다.View 의 set Minimum Width()방법 을 호출 하여 값 을 부여 할 수도 있 습 니 다.View 에 배경 이 존재 한다 면 View 자체 의 최소 너비 인 mMinWidth 와 View 배경의 최소 너비 중 최대 치 를 가 져 옵 니 다.ViewGroup 의 측정 과정
ViewGroup 의 Measure 과정 에 대해 ViewGroup 은 Measure 자체 의 크기 를 처리 하고 하위 View 를 옮 겨 다 니 며 그들의 measure 방법 을 호출 한 다음 에 각 하위 요 소 를 재 귀적 으로 Measure 과정 을 수행 해 야 합 니 다.ViewGroup 에 서 는 onMeasure 방법 을 다시 쓰 지 않 았 습 니 다.ViewGroup 은 추상 적 인 유형 이기 때문에 구체 적 인 ViewGroup 의 onMeasure 방법 에서 이 루어 지 는 과정 이 다 릅 니 다.그러나 뷰 그룹 에 서 는 하위 뷰 를 측정 하 는 measurement Children 방법 을 제공 했다.다음은 이 measure Children 방법 을 살 펴 보 겠 습 니 다.
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);
}
}
}
뷰 그룹의 모든 하위 뷰 를 가 져 옵 니 다.그리고 View Group 중성자 View 를 옮 겨 다 니 며 measure Child 방법 으로 하위 View 에 대한 측정 을 완성 합 니 다.다음은 measure Child 방법 을 살 펴 보 겠 습 니 다.
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
이 코드 에서 getChild MeasureSpec 방법 을 통 해 하위 View 너비 와 높 은 MeasureSpec 를 얻 을 수 있 습 니 다.그리고 서브 뷰 의 measure 방법 을 호출 하여 뷰 를 측정 하기 시작 했다.getChild MeasureSpec 방법 을 통 해 View 의 MeasureSpec 를 어떻게 얻 었 는 지 살 펴 보 자.
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
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;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
이 코드 에서 MeasureSpec 에 대한 획득 은 주로 부모 용기 의 MeasureSpec 와 View 자체 의 LayoutParams 에 의 해 이 루어 집 니 다.다음은 표 한 장 을 통 해 그들 간 의 대응 관 계 를 살 펴 보 자.여기까지 getChild Measure Spec 방법 을 통 해 하위 View 의 Measure Spec 를 얻 은 후 View 의 Measure 방법 을 호출 하여 View 를 측정 하기 시작 했다.
방금 말 한 바 와 같이 View Group 에 대해 추상 적 인 유형 으로 View 의 onMeasure 방법 을 다시 쓰 지 않 았 습 니 다.그러나 구체 적 인 View Group 에 도착 할 때,예 를 들 어 FrameLayout,LinearLayout,RelativeLayout 등 은 onMeasure 방법 을 다시 써 서 자신 과 하위 View 의 Measure 과정 을 완성 한다.다음은 FrameLayout 를 예 로 들 어 Measure 과정 을 살 펴 보 겠 습 니 다.FrameLayout 에서 도 Measure 과정 이 간단 한 편 입 니 다.다음은 FrameLayout 의 onMeasure 방법 을 살 펴 보 겠 습 니 다.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
이 부분 코드 에서 도 논리 가 간단 해서 주로 두 가지 일 을 완성 했다.먼저 FrameLayout 는 자신의 측정 과정 을 완성 한 다음 에 서브 View 를 옮 겨 다 니 며 View 의 measure 방법 을 실행 하여 View 의 Measure 과정 을 완성 합 니 다.여기 서 코드 는 비교적 간단 해서 상세 한 설명 을 하지 않 습 니 다.총결산
마지막 으로 View 와 View Group 의 Measure 과정 을 정리 하 겠 습 니 다.View 에 대해 서 는 Measure 가 간단 합 니 다.View 의 높이 와 너비 의 측정 치 를 얻 은 후에 높 고 넓 게 설정 합 니 다.한편,ViewGroup 의 경우 자신의 Measure 과정 을 완성 하 는 것 외 에 서브 View 를 옮 겨 다 니 며 서브 View 의 측정 과정 을 완성 해 야 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.