Android View 그리기 프로세스 04 - performDraw
앞의 두 장에서 각각 measure와layout의 절차를 배웠고 오늘은 그리는 절차의 마지막 단계draw를 배웠다.
4. ViewRootImpl.performTraversals()
ViewRootImpl.java 1576 줄
private void performTraversals() {
...
if (...) {
...
if (...) {
if (...) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
//
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
layoutRequested = true;
}
}
} else {
...
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) {
//
performLayout(lp, mWidth, mHeight);
...
}
...
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
...
// -------------
performDraw();
} else {
...
}
...
}
performDraw부터 따라가세요.performDraw로 들어가기()
4.1 ViewRootImpl.performDraw()
ViewRootImpl.java 2781 행
private void performDraw() {
...
draw(fullRedrawNeeded);
...
}
ViewRootImpl의 draw 방법을 다시 호출하여 계속 따라가기
4.2 ViewRootImpl.draw()
ViewRootImpl.java 2843 행
private void draw(boolean fullRedrawNeeded) {
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
...
}
코드를 분석한 결과, 여기에ViewRootImpl의drawSofware 방법이 또 호출되었다는 것을 알 수 있다.따라가다.
4.3 ViewRootImpl.drawSofware ()
ViewRootImpl.java 3022 행
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
...
mView.draw(canvas);
...
}
공식적으로 이 방법에 대한 설명은
드로잉이 성공하면 true이고 오류가 발생하면 false입니다.
이것도 4.2단계의 그if판단에 대응한다.그림 그리기에 성공했는지 판단하는 것일 거예요.여기 또 mView가 보인다.얘 뭔지 기억나?기억하지 못하는 친구는 아마도 앞의 몇 장에 가서 회상해야 할 것이다.이제 DecorView의 그림을 그릴 것입니다.그럼 DecorView의draw 방법에 들어가서 어떻게 그렸는지 봅시다.
4.4 DecorView.draw()
DecorView.java 784 행
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
코드가 매우 간단한 것을 보고 먼저 부류(Frame Layout)의draw 방법을 호출했다.그리고 자신의 메뉴 배경을 그립니다.(이건 팔로우를 하지 않고 필요하신 분들은 직접 들어가 보세요) 그런데 FrameLayout에 들어가보니 draw를 다시 쓰는 방법이 없어요.그럼 Frame Layout의 부류 View Group에 가서 다시 썼는지 확인해 보세요. 들어갔는데 없어요.그것은 아마도 최상급 부류 뷰에서만 있을 것이다.View에 들어가면 draw 방법이 있습니다.우리는 View의 draw 방법으로 넘어갑니다. (View의 draw 방법은 일반적으로 다시 쓰지 않습니다. 홈페이지 문서도 draw 방법을 다시 쓰지 말라고 권장합니다)
4.5 View.draw () 반환값은void
View.java 19088 행
/**
* 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.
*/
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
// .
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
...
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 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)
...
// Step 3, draw the content
...
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
...
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
...
}
왜 되돌아오는 값이void라는 것을 설명합니까? 뒤에 되돌아오는 값이booleaen인draw () 방법이 있기 때문입니다. 뒤에 공식적으로 이 방법에 대한 설명은 수동으로 이 View (그리고 모든 하위 View) 를 주어진 캔버스에 렌더링하는 것입니다.이 방법을 사용하기 전에, 이 View는 완전한 레이아웃을 완성해야 합니다.하나의 View를 실현할 때, 본 방법이 아닌 onDraw (android.graphics.Cavas) 를 실현해야 한다.만약 이 방법을 다시 쓸 필요가 있다면, 슈퍼 버전을 호출하십시오.매개 변수 canvas:view를 렌더링한 캔버스입니다.
해독: 사실 현재 View가 View 그룹이든 단일 View든 모두 이 절차를 실현해야 한다. 다른 것은 View 그룹에서dispatch Draw 방법을 실현했고 단일 View에서는 이 방법을 실현할 필요가 없다.사용자 정의 View는 일반적으로 onDraw 방법을 실현하고 그 중에서 서로 다른 스타일을 그려야 한다.
코드에서 많은 일을 하고 절차도 설정한 것을 알 수 있다.2단계와 5단계는 필요할 때만 사용(skip step 2 & 5 if possible(common case)
그렇다면 현재 관심사는 다음 3단계(Step6 전경과 스크롤 바를 그리면 스스로 알 수 있다)
Step1: drawBackground (canvas) 필요하면 배경을 그립니다.사실은 우리가 레이아웃 파일에 설정한android:background 속성입니다.이것은 그림을 그리기 시작하는 첫걸음이다.Step3: onDraw(canvas)가 View의 내용(중점)을 그립니다.Step4: dispatchDraw(canvas) 그림 하위 뷰.이 방법은 ViewGroup을 도와 하위 View를 차례로 그리는 데 사용됩니다
주: Step1과 Step3은 호출되기 전에 모두 하나의 판단을 했다. 즉,drawBackground와 onDraw 방법이 반드시 호출되는 것은 아니라는 것이다.이 판단은 무슨 뜻인가요?dirtyOpaque 로고 비트의 역할은 현재 View가 투명한지 판단하는 것입니다. 투명하다면drawBackground와onDraw를 실행하지 않습니다.dispatchDraw를 직접 실행하여 내부의 하위 View를 그립니다.즉,dirtyOpaque 로고 비트가false(투명, 비실심)여야 이 두 가지 방법을 사용할 수 있다.
그럼 먼저 View의 draw Back ground 방법에 따라가세요.
4.6 View.drawBackground ()
View.java 19302 행
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
// ,
if (background == null) {
return;
}
...
background.draw(canvas);
...
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
// Drawable draw
background.draw(canvas);
} else {
//
canvas.translate(scrollX, scrollY);
// Drawable draw
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
배경을 설정하지 않으면 바로return입니다.배경을 설정하면 Drawable이 호출됩니다.draw 방법으로 그려주세요.여기는 더 이상 따라가지 않겠습니다. 우리는 온 드래곤과 디스패치 드래곤 두 가지 방법에 중점을 두어야 합니다.onDraw가 그림을 그리는 내용이라면 구체적인 자류가 이루어져야 한다.그럼 바로 DecorView로 들어가세요.onDraw
4.7 DecorView.onDraw()
DecorView.java 315 행
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
mBackgroundFallback.draw(isResizing() ? this : mContentRoot, mContentRoot, c,mWindow.mContentParent);
}
슈퍼를 먼저 호출했습니다.onDraw 방법, 그리고 뒤에 자기 물건을 그렸어요.추적 결과 Frame Layout도 onDraw 방법을 실현하지 못했고 View Group도 없었다.이곳의 슈퍼를 말하는 것과 같다.onDraw가 호출한 것은 View입니다.onDraw (), 이제 View로 들어갑니다.onDraw()
4.8 View.onDraw()
View.java 16827 행
protected void onDraw(Canvas canvas) {
}
View의 onDraw (canvas) 는 빈 방법입니다.각 View의 내용은 각기 다르기 때문에 하위 클래스에서 구체적인 논리를 실현해야 한다.위에 있는 데코뷰처럼.onDraw () 방법과 마찬가지로 onDraw () 방법을 실현한 다음에 메뉴 배경을 그렸습니다.DecorView는 여기까지 와서 자신을 다 그렸다.이어서 디스패치 Draw () 방법을 사용해서 서브View를 그리기 시작했다.
그럼 먼저 View를 봅시다.dispatchDraw () 에서 무엇을 했는지 들어가서 보니 빈 방법입니다.아무 일도 하지 않았어요.View처럼.onDraw처럼.왜 그랬을까?잎 뷰에 대해 말하자면, 이 방법은 아무런 의미가 없기 때문에, 그 자체가 잎 뷰이고, 자 뷰가 없으면 그려야 한다.그러나 View Group의 경우 하위 View를 그리기 위해 이 방법을 다시 써야 한다.
그럼 DecorView에서 dispatchDraw () 방법을 보십시오.이 방법을 다시 쓰지 않았고, Frame Layout도 다시 쓰지 않았고, View Group까지 따라가서야 이 방법을 찾았다.따라서DecorView가onDraw () 방법을 실행하면 절차는ViewGroup의dispatchDraw () 방법에 커집니다.(원본에서 평소 자주 사용하던 LinearLayout, FrameLayout, RelativeLayout 등 자주 사용하는 레이아웃 컨트롤은 다시 쓰는 방법이 없음을 발견했다.)
4.9 ViewGroup.dispatchDraw()
ViewGroup.java 3927 행
@Override
protected void dispatchDraw(Canvas canvas) {
...
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
...
for (int i = 0; i < childrenCount; i++) {
...
if (...) {
more |= drawChild(canvas, child, drawingTime);
}
...
}
...
}
귀속자 View의 순환을 다시 보았습니다.순환에서 ViewGroup의drawChild 방법을 호출하고 하위 View 대상을 전송합니다.(DecorView의 첫 번째 하위 VIew는 LinearLayout.)이제 ViewGroup으로 들어갑니다.drawChild() 방법
4.10 ViewGroup.drawChild()
ViewGroup.java 4213 행
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
이 방법에 대한 공식적인 설명을 먼저 보도록 하겠습니다.
현재 ViewGroup의 하위 View를 그립니다.이 방법은 정확한 상태에서 화포를 얻는 것을 책임진다.여기에는 하위 뷰의 스크롤 원점이 0.0에 있도록 트림, 이동, 애니메이션 변환이 포함됩니다.매개 변수1: 캔버스 매개 변수2: 그려야 할 서브View 매개 변수3: 발생하는 시간점을 그립니다.
주의: 여기 호출된draw () 방법은 반환 값이 있는 방법입니다. 위에서 우리가 호출한draw () 방법은 모두 반환 값이 없는 것을 보았습니다.DecorView의 서브뷰는 Linear Layout입니다. Linear Layout에 들어간 후에 이 방법을 다시 쓰지 않았습니다. View Group에서도 다시 쓰지 않았습니다. 그러면 틀림없이 View에 있을 것입니다.View로 이동합니다.Draw () 가 boolean으로 되돌아오는 방법입니다.
4.11 View.draw () 반환 값은 boolean입니다.
View.java 18760 행
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
...
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// View , View.
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
} else {
draw(canvas);
}
...
return more;
}
이 메서드에 대한 공식 참고 사항은 다음과 같습니다.
이 메서드는 ViewGroup입니다.drawChild () 방법이 호출되었습니다.각 하위 뷰에 대해 자신을 그립니다.뷰는 도면층 형식 (cpu 또는 gpu) 에 따라 렌더링과 하드웨어 가속을 진행합니다.
코드에서 생략된 부분은 다음과 같습니다.
ViewGroup에서 하위 View를 아래로 나누어 줄 때 하드웨어 가속과 View의 그리기 유형을 켜는지 여부에 따라 현재 View가 CPU로 그리는지 GPU로 그리는지 판단한다. GPU의 주요 역할은 바로 도형을 렌더링하는 것이다. 도형을 그리는 속도는 CPU보다 높지만 GPU는 CPU보다 전기를 더 많이 소모한다.
주: 코드에서 보면 먼저 판단이 나옵니다. 이 판단은 위의voiddraw() 방법과 유사하며 모두 mPrivateFlags와 관련이 있습니다.mPrivateFlags에 PFLAG가 포함된 경우SKIP_DRAW는 현재 뷰의 하위 뷰를 그리기 위해 디스패치 Draw를 직접 호출합니다.포함하지 않으면 현재 View에서 반환 값이 void인 draw () 방법을 호출합니다.void draw () 방법의 몇 가지 절차를 계속 실행합니다
PFLAG 포함 여부SKIP_DRAW, 여기는 더 이상 설명이 없습니다. 안드로이드 View Group의draw와onDraw의 호출 시기를 정말 잘 알고 계신지 참고하세요. 이 글은 아주 명확하게 소개되어 있습니다.
이제 뷰의 그리기 프로세스가 거의 끝나지 않았습니다.다음은draw의 과정을 그림으로 묘사합니다.
preforDraw 프로세스
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.