본문 바로가기

Android

[Android] XML이 View를 그리기까지의 과정

 

안드로이드 개발을 하다 보면 XML에 View를 선언하고 setContentView() 한 줄만 호출해도 화면이 그려집니다.

(물론 요즘은 XML 보단 Compose를 많이 사용하는 추세이지만, XML을 알고 Compose를 알면 더 좋을 것 같아서 XML의 과정을 먼저 정리해보려 합니다.)

 

하지만 막상 XML이 실제 View 객체가 되고, 화면에 그려지기까지의 흐름을 정확히 설명하려고 하면 애매해지는 경우가 많습니다.

이번 글에서는 XML → View 객체 → Measure → Layout → Draw 이 전체 과정을 흐름 위주로 정리해보겠습니다.

 

 

 

1️⃣ setContentView()가 호출되면 무슨 일이 벌어질까?

Activity에서 가장 흔히 보는 코드부터 시작합니다.

setContentView(R.layout.activity_main)

 

이 한 줄이 호출되면 내부적으로는 다음 작업이 일어납니다.

  1. XML 리소스를 읽어온다
  2. XML을 파싱한다
  3. View 객체 트리를 생성한다
  4. DecorView에 붙인다

이 모든 작업의 중심에 있는 게 바로 LayoutInflater입니다.

 

 

 

2️⃣ LayoutInflater : XML을 View로 바꾸는 공장

LayoutInflater의 역할은 단순합니다.

XML 파일을 실제 View 객체 트리로 변환

 
val view = LayoutInflater.from(context) .inflate(R.layout.activity_main, parent, false)

 

inflate 내부에서 하는 일은 다음과 같습니다.

  • XML 태그를 한 줄씩 읽기
  • 태그 이름으로 View 클래스 찾기
  • Reflection으로 View 생성자 호출
  • layout_width, layout_height 같은 속성 적용
  • 부모-자식 관계 설정

 

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

 

예를 들어 위와 같은 코드의 경우, 내부적으로는 아래와 같은 느낌이라고 볼 수 있습니다.

TextView(context, attrs)

 

즉, XML은 View 설계도가 되고, LayoutInflater는 설계도를 실제 객체로 찍어내는 공장이라고 비유해 볼 수 있습니다!

이렇게 보니 조금 더 쉽게 다가오죠?

 

 

 

3️⃣ View 트리는 어디에 붙을까? (DecorView)

그러나 이때, inflate로 만들어진 View 트리는 곧바로 화면에 그려지지 않습니다.

구조는 대략 아래와 같습니다

DecorView
 └─ LinearLayout
     ├─ ActionBar
     └─ ContentView (setContentView로 넣은 레이아웃)
  • DecorView는 윈도우의 루트 View
  • 상태바, 네비게이션바, 액션바까지 포함

우리가 만든 레이아웃은 DecorView 안쪽 Content 영역에 삽입됩니다.

 

 

그리고 View가 생겼다고 바로 그려지는 것 또한 아닙니다!

View가 화면에 보이려면 반드시 다음 3단계를 거칩니다.

Measure → Layout → Draw

 

이걸 흔히 View Rendering Pipeline이라고 합니다.

 

 

 

5️⃣ Measure 단계 : “이 View, 크기가 얼마야?”

measure() 단계에서는 각 View가 자신의 크기를 계산합니다.

이때 중요한 것들이 있습니다.

  • layout_width, layout_height
  • wrap_content, match_parent
  • 부모 View가 준 MeasureSpec
onMeasure(widthMeasureSpec, heightMeasureSpec)

 

부모가 자식에게 “이 안에서 크기 정해” 하고 제약을 전달합니다.

그럼 이 단계에서 measuredWidth / measuredHeight가 결정됩니다.

 

 

 

6️⃣ Layout 단계 : “어디에 놓을까?”

크기가 정해졌다면 이제 위치를 정해야 합니다.

 
onLayout(changed, l, t, r, b)
  • 부모 View가 자식 View들을 순회
  • 각 View의 좌표(left, top, right, bottom) 결정

크기는 Measure을 의미하고, 위치는 Layout을 의미하게 됩니다.

 

 

7️⃣ Draw 단계 – “마지막 그리기 단계”

드디어 마지막으로 실제 화면에 그리는 단계입니다.

 

onDraw(canvas: Canvas)

 

여기서 배경, 텍슽, 이미지, 커스텀 드로잉 등 모든 픽셀이 그려지게 됩니다.

그리고 이 단계가 끝나면 우리가 흔히 보는 화면이 됩니다.

 

마지막으로 총 정리를 해보자면 아래와 같습니다!

 

XML
 ↓
LayoutInflater (XML 파싱)
 ↓
View 객체 트리 생성
 ↓
DecorView에 attach
 ↓
Measure (크기 계산)
 ↓
Layout (위치 계산)
 ↓
Draw (화면에 그림)

 

 

코드를 짤 때에는 `setContentView()` 함수 하나로 간편하게 화면을 그렸었는데, 화면이 그려지는 과정을 자세히 들여다보니

모르고 있던 디테일들이 많은 것 같습니다.

 

XML은 단순한 UI 선언처럼 보이지만, 그 뒤에는 꽤 많은 단계와 규칙이 숨어 있습니다.

이 흐름을 한 번이라도 명확히 이해하고 나면 UI 버그를 만났을 때 막연한 추측이 아니라 의심 지점부터 정확히 짚을 수 있게 되겠죠?

 

모든 디버깅은 기초를 알아야 정확히 할 수 있는 것 같습니다 ㅎㅎ

 

 

 

📚 참고

https://developer.android.com/guide/topics/ui/how-android-draws?hl=ko

 

Android에서 뷰를 그리는 방법  |  Views  |  Android Developers

활동이 포커스를 받으면 레이아웃을 그리라는 요청을 받습니다. Android 프레임워크에서 그리기 절차를 처리하지만 활동에서 레이아웃 계층 구조의 루트 노드를 제공해야 합니다. 그리기는 레이

developer.android.com

 

https://developer.android.com/reference/android/view/View

 

View  |  API reference  |  Android Developers

 

developer.android.com