티스토리 뷰

안드로이드

LifeCycle Of View

pjm1n 2025. 12. 9. 01:24

XML UI

View

UI를 구성하는 가장 작은 단위를 말한다. 예를 들면 TextView, ImageView, Button 등이 있다.

ViewGroup

View를 여러 개 포함할 수 있는 컨테이너 개념이다. 예를 들면 LinearLayout, ConstraintLayout 등이 있다.

ViewGroup 자체도 View의 일종이지만, 자식 View들의 Measure, Layout을 담당한다.

View Tree 구조

다음과 같이 XML으로 구성된 화면은 내부적으로 트리 구조로 이루어져 있다.

<LinearLayout>
    <TextView />
    <Button />
</LinearLayout>

이 트리는 단순히 XML → 화면에 그려짐 처럼 동작하지 않는다. 그럼 어떻게 이 XML 코드가 View로 변경되고 화면에 그려질까?

XML이 View 객체가 되는 과정

XML은 UI를 정의하는 설계 파일이다. 이 XML 파일을 통해 Binary XML로 변환되고, 런타임에 LayoutInflater가 이를 해석해 실제 View 객체 트리를 생성한다.

아래와 같이 우리가 Activity나 Fragment에서 흔히 볼 수 있는 코드가 있다.

setContentView(R.layout.activity_main)

XML → Binary XML

XML은 우리가 아는 사람이 읽을 수 있는 텍스트 형식의 파일이고 Binary XML은 안드로이드가 읽을 수 있도록 컴파일된 이진 포맷이다.

앱을 빌드하면 AAPT2가 XML파일을 분석하여 Binary XML로 컴파일한다.

Binary XML → View 객체(Inflate)

  1. LayoutInflater가 Binary XML을 읽는다.
  2. XML 태그를 해당 View 클래스로 매핑하여 View 객체를 생성한다.<LinearLayout> → new LinearLayout(context, attrs)
  3. ex) <TextView> → new TextView(context, attrs)
  4. 생성된 View 객체는 AttributeSet을 통해 고유 속성(ex: text, textColor 등)을 초기화 하고 layout_width, layout_height 등의 layout 속성은 나주에 부모 ViewGroup이 별도로 적용한다.
  5. ViewGroup이라면 LayoutParams를 생성하여 layout_height, layout_width 등 layout_** 속성을 저장한다.
  6. parent.addView(view, params)를 통해 부모가 자식 View와 LayoutParams를 연결한 후, onViewAdded(view)가 호출된다.
  7. XML 트리 구조에 맞게 2~5 과정을 재귀적으로 반복한다.
  8. 이 후 onFinishInflate()가 호출된다. 이는 inflate를 완료했음을 알리는 콜백이다.

예시

<LinearLayout>
    <TextView />
    <LinearLayout>
        <Button />
    </LinearLayout>
</LinearLayout>
1. XML 파일 오픈 (딱 1번)

★ 2. <LinearLayout> → new LinearLayout(context, attrs)
★ 3. 속성 적용
(루트라 부모 없음 → LP 생성 없음)
(루트라 addView 없음)

   ↓ children 탐색 시작

   ★ 2. <TextView> → new TextView(context, attrs)
   ★ 3. 속성 적용
   ★ 4. 부모 LinearLayout이 LayoutParams 생성
   ★ 5. 부모가 addView

   ★ 2. <LinearLayout> → new LinearLayout(context, attrs)
   ★ 3. 속성 적용
   ★ 4. 부모 LinearLayout이 LayoutParams 생성
   ★ 5. addView

       ↓ children 탐색

       ★ 2. <Button> → new Button(context, attrs)
       ★ 3. 속성 적용
       ★ 4. 부모 LinearLayout이 LayoutParams 생성
       ★ 5. addView

7. 모든 자식 완료 후 루트 LinearLayout.onFinishInflate()

화면에 View 그리기

생성된 View 객체는 아직 화면에 그려지지 않았다. 다음 흐름에 맞게 화면에 그려지고 파괴된다.

  1. onAttachedToWindow
  2. onMeasure
  3. onLayout
  4. onDraw
  5. onDetachedFromWindow

1. onAttachedToWindow

View가 윈도우에 실제 부착될 때 호출된다.

여기서는 windowInsets 요청, Keyboard/IME 처리, ViewTree, Observer 등록을 하며, 아직 화면의 크기는 확정되지 않았다.

2. onMeasure

부모 View가 자식 View에게 MeasureSpec을 전달하며 시작된다.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

MeasureSpec은 다음 정보를 담는다.

  • 부모 MeasureSpec 모드
    • EXACTLY: “이 크기로 무조건 측정해라.”
    자식 layout_* 자식 MeasureSpec 모드 size
    match_parent EXACTLY 부모 size
    wrap_content AT_MOST 부모 size
    정확한 dp 값 EXACTLY 그 dp를 px로 변환한 값
    • AT_MOST: “최대 이 크기까지 가능하다.”
    자식 layout_* 자식 MeasureSpec 모드 size
    match_parent AT_MOST 부모 size
    wrap_content AT_MOST 부모 size
    정확한 dp 값 EXACTLY dp → px
    • UNSPECIFIED: “크기 제한이 없다.”
    자식 layout_* 자식 MeasureSpec 모드 size
    match_parent UNSPECIFIED 0 (또는 제약 없음)
    wrap_content UNSPECIFIED 0 (또는 제약 없음)
    정확한 dp 값 EXACTLY dp → px

View는 이 제약조건을 기반으로 자신의 크기를 계산해 setMeasureDimension(width, height)에 결과를 기록한다.

3. onLayout

protected void onLayout(boolean changed, int left, int top, int right, int bottom)

Measure 결과를 토대로 자식의 실제위치(left, top, right, bottom)을 결정한다.

ViewGroup은 OnLayout() 내에서 자식들 각각에 대해 child.layout(left, top, right, bottom) 호출하며 좌표는 부모기준이다.

Layout 단계가 끝나면 화면의 구조가 완전히 결정된다.

4. onDraw

Canvas에 픽셀을 그린다. Canvas는 그림을 그리는 도화지라 생각하면 된다.

순서는 다음과 같다.

  1. 부모 ViewGroup 그리기
    • background 그리기
    • padding 영역 그리기
  2. 자신을 그리기
    • 텍스트, 이미지, 도형 그리기
  3. 자식 그리기

5. onDetachedFromWindow

화면에서 View가 사라질 때 호출된다. 이때 Listener, Observer, 코루틴, 애니메이션 등을 정리한다.

invalidate() VS requestLayout()

invalidate()

화면에 그려진 모양이 바뀌었을 때 호출되며 draw()만 호출된다.

ex) 색상, 외곽선, 애니메이션의 픽셀 변화 등

requestLayout()

View의 크기나 위치, 레이아웃이 바뀔 때 호출된다. 따라서 측정부터 다시해야하므로 measure → layout → draw 모두 다시 호출된다.

ex) layout_width, layout_height 변경, padding, margin 변경, Constraint 변경 등

 

대부분 애니메이션은 픽셀이 변화하는데도 requestLayout()이 호출되지 않는다. 이유는 Canvas 상에서 그림만 이동/회전/축소/확대하는 것이지 View의 실제 너비와 높이는 그대로 유지되기 때문이다. 하지만 LayoutParams를 직접 변경한 경우는 requestLayout()이 호출된다.

텍스트가 바뀌면 invalidate()와 requestLayout()이 모두 호출된다. 이유는 텍스트 길이에 따라 크기가 변할 수도 있고, 안변할 수도 있기 때문이다.

'안드로이드' 카테고리의 다른 글

Hilt, Metro의 DI 그래프 생성, 등록, 주입 차이  (0) 2025.12.08
Metro로 ViewModel 생성하기  (0) 2025.11.06
Lottie  (0) 2025.11.05
LifeCycle Of ViewModel  (1) 2025.10.05
Android Thread 통신과 Handler·Looper  (4) 2025.08.29
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함