Android View 그리 기 메커니즘 에 대한 상세 한 설명
View 트 리 의 그래 픽 프로 세 스
Activity 가 초점 을 받 았 을 때 레이아웃 을 그 려 달라 고 요청 합 니 다.이 요청 은 Android framework 에서 처리 합 니 다.그 리 는 것 은 루트 노드 부터 레이아웃 트 리 를 measure 와 draw 합 니 다.전체 View 트 리 의 그래 픽 프로 세 스 는 ViewRoot.java 류 의 performTraversals()함수 에서 펼 쳐 집 니 다.이 함수 가 하 는 작업 은 보기 크기(measure)를 다시 계산 해 야 하 는 지,보기 의 위치(layot)를 다시 설정 해 야 하 는 지,다시 그 려 야 하 는 지(draw)로 간단하게 요약 할 수 있 습 니 다.프로 세 스 도 는 다음 과 같 습 니 다.
보기 프로 세 스 함수 호출 체인 그리 기
설명 이 필요 한 것 은 사용자 가 자발적으로 request 를 호출 하면 measure 와 layot 과정 만 출발 하고 draw 과정 을 실행 하지 않 습 니 다.
개념
1.measure 와 layot
전체적으로 Measure 와 Layout 두 단계 의 실행:
트 리 의 옮 겨 다 니 는 것 은 질서 가 있 습 니 다.부모 보기에 서 하위 보기 까지 모든 ViewGroup 은 모든 하위 보 기 를 측정 하고 맨 밑 에 있 는 View 는 자신 을 측정 합 니 다.
2.구체 적 분석
measure 프로 세 스 는 measure(int,int)방법 으로 시작 하여 위 에서 아래로 질서 있 게 View 를 측정 하고 measure 프로 세 스 의 마지막 에 각 보기 마다 자신의 크기 와 측정 규격 을 저장 합 니 다.
layot 프로 세 스 는 layot(int,int,int,int)방법 으로 시작 되 며 위 에서 아래로 옮 겨 다 닙 니 다.이 과정 에서 모든 부모 보 기 는 measure 과정 에서 얻 은 크기 에 따라 자신의 하위 보 기 를 배치 합 니 다.
measure 과정 은 하나의 View 와 모든 하위 노드 의 mMeasured Width 와 mMeasured Height 변수 에 값 을 부여 합 니 다.이 값 은 getMeasured Width()와 getMeasured Height()방법 으로 얻 을 수 있 습 니 다.그리고 이 두 값 은 부모 보기 의 제약 범위 안에 있어 야 모든 부모 보기 가 모든 하위 보기 의 측정 을 받 을 수 있 습 니 다.하위 보기 가 Measure 가 얻 은 크기 가 만 족 스 럽 지 않 을 경우 부모 보기 가 개입 하여 측정 규칙 을 설정 하여 두 번 째 measure 를 진행 합 니 다.예 를 들 어 부모 보 기 는 주어진 dimension 에 따라 모든 하위 보 기 를 측정 할 수 있 습 니 다.최종 하위 보기 의 제약 되 지 않 은 크기 가 너무 크 거나 너무 작 을 때 부모 보 기 는 정확 한 크기 로 다시 하위 보 기 를 measure 할 수 있 습 니 다.
3.measure 프로 세 스 전달 사이즈 의 두 가지 유형
이 종 류 는 보기 의 높이 와 너비 등 인 자 를 지정 하 는 데 자주 사용 된다.각 보기 의 height 와 width 에 대해 다음 과 같은 선택 이 있 습 니 다.
ViewGroup 의 하위 클래스 는 ViewGroup.LayoutParams 의 하위 클래스 가 있 습 니 다.예 를 들 어 RelativeLayout 가 가지 고 있 는 ViewGroup.LayoutParams 의 하위 클래스 RelativeLayoutParams.
때때로 우 리 는 view.getLayoutParams()방법 을 사용 하여 보기 LayoutParams 를 가 져 와 서 강전 환 을 해 야 하지만,구체 적 인 유형 을 모 르 기 때문에 강전 오류 가 발생 할 수 있 습 니 다.사실 이 방법 은 부모 보기 형식의 LayoutParams 를 얻 었 습 니 다.예 를 들 어 View 의 부모 컨트롤 이 RelativeLayout 이면 얻 은 LayoutParams 유형 은 RelativeLayoutParams 입 니 다.
MeasureSpecs
측정 규격 은 측정 요구 와 사이즈 에 대한 정 보 를 포함 하고 세 가지 모델 이 있 습 니 다.
UNSPECIFIED
부모 보 기 는 하위 보기 에 제약 이 없습니다.원 하 는 임의의 크기 에 도달 할 수 있 습 니 다.예 를 들 어 ListView,ScrollView 는 일반적으로 사용자 정의 View 에서 사용 할 수 없습니다.
EXACTLY
부모 보 기 는 하위 보기 로 정확 한 사 이 즈 를 지정 합 니 다.또한 하위 보기 가 아무리 기대 하 더 라 도 이 지정 한 크기 의 경계 내 에서 해당 하 는 속성 은 match 입 니 다.parent 또는 구체 적 인 값,예 를 들 어 100 dp,부모 컨트롤 은
MeasureSpec.getSize(measureSpec)
을 통 해 하위 컨트롤 의 사 이 즈 를 직접 얻 을 수 있 습 니 다.AT_MOST
부모 보 기 는 하위 보기 의 최대 사 이 즈 를 지정 합 니 다.하위 보 기 는 자신의 모든 하위 보기 가 이 사이즈 범위 내 에 적응 할 수 있 도록 확보 해 야 합 니 다.대응 하 는 속성 은 wrap 입 니 다.콘 텐 츠,이러한 모드 에서 부모 컨트롤 은 하위 View 의 사 이 즈 를 확정 할 수 없 으 며,하위 컨트롤 은 자신 이 필요 에 따라 자신의 사 이 즈 를 계산 할 수 밖 에 없습니다.이러한 모델 은 바로 우리 가 사용자 정의 보 기 를 통 해 측정 논 리 를 실현 해 야 하 는 상황 입 니 다.
3.measure 핵심 방법
measure(int widthMeasureSpec, int heightMeasureSpec)
이 방법 은
View.java
클래스 에서 final 형식 으로 복사 할 수 없 지만 measure 호출 체인 은 최종 적 으로 View/View Group 대상onMeasure()
방법 을 되 돌려 줍 니 다.따라서 보 기 를 사용자 정의 할 때 복사onMeasure()
방법 만 있 으 면 됩 니 다.onMeasure(int widthMeasureSpec, int heightMeasureSpec)
이 방법 은 보기 에서 측정 논 리 를 실현 하 는 방법 을 사용자 정의 하 는 것 입 니 다.이 방법의 매개 변 수 는 부모 보기 가 하위 보기 의 width 와 height 에 대한 측정 요구 입 니 다.사용자 정의 보기에 서 해 야 할 일 은 이 widthMeasureSpec 과 height MeasureSpec 에 따라 보기 의 width 와 height 를 계산 하 는 것 입 니 다.패턴 처리 방식 이 다 릅 니 다.
setMeasuredDimension()
측정 단계 의 최종 방법 은
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
방법 에서 호출 하여 계 산 된 사 이 즈 를 이 방법 에 전달 하면 측정 단계 가 곧 끝난다.이 방법 도 반드시 호출 해 야 하 는 방법 이다.그렇지 않 으 면 이상 을 보고 할 것 이다.사용자 정의 보 기 를 사용 할 때 시스템 의 복잡 한 Measure 과정 에 관심 을 가 질 필요 가 없습니다.Measure Spec 에 따라 계 산 된 사 이 즈 를 호출setMeasuredDimension()
하면 됩 니 다.ViewPager Indicator 의 onMeasure 방법 을 참고 하 십시오.다음은 View Group 의
measureChildren(int widthMeasureSpec, int heightMeasureSpec)
방법 으로 복합 View 의 Measure 절 차 를 분석 하 겠 습 니 다.Measure Child 방법 호출 프로 세 스 맵:
소스 코드 분석
/**
* View measure , View MeasureSpec padding
* GONE View, getChildMeasureSpec
*
* @param widthMeasureSpec View width
* @param heightMeasureSpec View height
*/
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);
}
}
}
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();// Child LayoutParams
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,// ChildView widthMeasureSpec
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,// ChildView heightMeasureSpec
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
/**
* measureChildren , ChildView MeasureSpec。
* ChildView MeasureSpec LayoutParams 。
*
* @param spec View
* @param padding View paddingand, margins
*
* @param childDimension (height width)
* @return MeasureSpec
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
.........
// MeasureSpec
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
/**
*
* View , 、
* View onMeasure(int, int) , 。
* , onMeasure(int, int)
*
* @param widthMeasureSpec , Measure
* @param heightMeasureSpec , Measure
*
*/
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
4.layot 관련 개념 및 핵심 방법우선 하위 보기 의 구체 적 인 위 치 는 부모 보기 에 비해 명확 하 다.View 의 onLayout 방법 은 비어 있 고 ViewGroup 의 onLayout 는 abstract 이기 때문에 사용자 정의 View 가 ViewGroup 을 계승 하려 면 onLayout 함 수 를 실현 해 야 합 니 다.
layot 과정 에서 하위 보 기 는
getMeasuredWidth()
와getMeasuredHeight()
방법 으로 measure 과정 에서 얻 은 mMeasured Width 와 mMeasured Height 를 자신의 width 와 height 로 호출 합 니 다.그리고 각 하위 보기 의layout(l, t, r, b)
함 수 를 호출 하여 각 하위 보기 가 부모 보기 의 위 치 를 확인 합 니 다.LinearLayout 의 onLayout 소스 코드 분석
@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);
}
}
/**
* View,
*/
void layoutVertical(int left, int top, int right, int bottom) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {// View GONE
final int childWidth = child.getMeasuredWidth();//measure Width
final int childHeight = child.getMeasuredHeight();//measure height
... childLeft、childTop
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
}
}
}
private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}
View.java
public void layout(int l, int t, int r, int b) {
...
setFrame(l, t, r, b)
}
/**
* View
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
...
}
5.프로 세 스 관련 개념 및 핵심 방법 그리 기먼저 draw 과정 과 관련 된 함 수 를 살 펴 보 겠 습 니 다.
View.draw(Canvas canvas):
ViewGroup 은 이 방법 을 복사 하지 않 았 기 때문에 모든 보 기 는 최종 적 으로 View 의 draw 방법 으로 그립 니 다.사용자 정의 보기에 서도 이 방법 을 복사 하 는 것 이 아니 라 복사
onDraw(Canvas)
방법 으로 그 려 야 합 니 다.사용자 정의 보기 가 이 방법 을 복사 하려 면super.draw(canvas)
를 호출 하여 시스템 그리 기 를 완성 한 다음 사용자 정의 그리 기 를 하 십시오.View.onDraw():
View 의
onDraw(Canvas)
기본 값 은 비어 있 습 니 다.그리 기 과정 에서 복사 해 야 할 방법 을 사용자 정의 하고 자신의 내용 을 그립 니 다.dispatchDraw()
하위 보기 그리 기 시작 합 니 다.View 에서 기본 값 은 비어 있 습 니 다.ViewGroup 은 하위 보 기 를 그 리 려 고 복사
dispatchDraw()
했 습 니 다.이 방법 은 상관 하지 않 아 도 됩 니 다.사용자 정의 View Group 은dispatchDraw()
을 복사 해 서 는 안 됩 니 다.흐름 도 그리 기
View.draw(Canvas)소스 코드 분석
/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
* called. When implementing a view, implement
* {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
* If you do need to override this method, call the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*
* Canvas View( View)。 layout。 view ,
* onDraw(Canvas) , draw(canvas) 。 , 。
*/
public void draw(Canvas canvas) {
/ * Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background if need
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children (dispatchDraw)
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// we're done...
return;
}
// Step 2, save the canvas' layers
...
// Step 3, draw the content
if (!dirtyOpaque)
onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);
}
위의 처리 과정 에서 우 리 는 최적화 된 작은 기 교 를 얻 을 수 있다.레이 어 를 그 릴 필요 가 없 을 때 두 번 째 단계 와 다섯 번 째 단 계 는 건 너 갈 수 있다.따라서 그림 을 그 릴 때 절약 할 수 있 는 layer 는 절약 할 수 있 고 그리 기 효율 을 높 일 수 있 습 니 다.ViewGroup.dispatchDraw()소스 코드 분석
dispatchDraw(Canvas canvas){
...
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {// ChildView
final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < childrenCount; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {// Visible ,
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, childrenCount);//
bindLayoutAnimation(child);// Child
if (cache) {
child.setDrawingCacheEnabled(true);
if (buildCache) {
child.buildDrawingCache(true);
}
}
}
}
final LayoutAnimationController controller = mLayoutAnimationController;
if (controller.willOverlap()) {
mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
}
controller.start();// View
}
// ChildView
for (int i = 0; i < childrenCount; i++) {
int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
...
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
/**
* This method is called by ViewGroup.drawChild() to have each child view draw itself.
* This draw() method is an implementation detail and is not intended to be overridden or
* to be called from anywhere else other than ViewGroup.drawChild().
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
...
}
drawChild(canvas, this, drawingTime) View 의
child.draw(canvas, this,drawingTime)
방법 을 직접 호출 했 습 니 다.문서 에서 도 설명 되 었 습 니 다.ViewGroup.drawChild()
방법 을 제외 하고 다른 곳 에서 복사 하거나 호출 해 서 는 안 됩 니 다.이것 은 View Group 에 속 합 니 다.View.draw(Canvas)
방법 은 우리 가 사용자 정의 컨트롤 에서 복사 할 수 있 는 방법 으로 구체 적 으로 상기view.draw(Canvas)
에 대한 설명 을 참고 할 수 있 습 니 다.매개 변수 에서 볼 수 있 듯 이child.draw(canvas, this, drawingTime)
부모 보기 와 관련 된 논 리 를 처리 한 것 이 분명 하지만 View 의 최종 그리 기 또는View.draw(Canvas)
방법 입 니 다.invalidate()
View 트 리,즉 draw 프로 세 스 를 다시 그 려 달라 고 요청 합 니 다.보기 의 크기 가 변 하지 않 으 면
layout()
프로 세 스 를 호출 하지 않 고invalidate()
방법 을 호출 한 View 만 그립 니 다.requestLayout()
레이아웃 이 변 할 때,예 를 들 어 방향 변화,사이즈 변화 등 은 이 방법 을 사용 합 니 다.사용자 정의 보기에 서 사이즈 크기 를 재 측정 하려 면 수 동 으로 이 방법 을 사용 해 야 합 니 다.
measure()
과layout()
과정 을 촉발 하지만 draw 를 하지 않 습 니 다.읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.