[Android] View의 생명주기

문승연·2024년 2월 17일
0

Android 기초

목록 보기
7/8
post-thumbnail

본 포스트는 Android에서 View의 생명주기 해당 블로그의 글을 필자가 쉽게 이해하고 기록하기 위한 용도로 작성한 글입니다. 자세한 내용은 해당 블로그를 참고해주세요.

1. View란?

안드로이드 앱에서 View는 유저 인터페이스를 구성요소를 갖고있는 가장 기본적인 단위 클래스이다. View는 화면 상에서 사각형 영역을 가지며 그리기이벤트 처리 등을 처리할 수 있다. 또한 View는 버튼이나 텍스트 필드와 같은 유저 상호작용 요소들의 집합인 Widget의 가장 기초 클래스이다.

View의 서브 클래스인 ViewGroup은 레이아웃들의 기초 클래스이며 View들(또는 다른 ViewGroup)을 담는 보이지 않는 컨테이너 역할을 한다.

안드로이드의 ActivityFragment에 생명주기가 존재하는 것처럼 View에도 역시나 생명주기가 존재한다. 하지만 많은 개발자들이 View의 생명주기를 제대로 알지 못하고 개발을 한다. (사실 나도 그랬다...)

이번 포스트에서는 View의 생명주기와 각 단계에서 일어나는 동작에 대해 알아보고자 한다.

2. View의 생명주기

화면에 렌더링 된 View는 다음 그림과 같은 생명주기 메서드를 거쳐 화면에 그려진다.

위 생명주기의 각 단계들은 모두 중요한 역할을 하고 있다. 각각의 단계에서 어떤 일이 일어나는지 알아보자.

1. 생성자(Constructors)

커스텀 뷰(Custom View)를 만들때 생성자를 어떤 걸 써야하는지 난감할 때가 많다. View의 생성자가 여러 종류가 있는데다 각각 어떤 차이점이 있는지 명확히 모르기 때문이다.

  1. View(Context context)
    코드에서 View를 동적으로 만들 때 사용하는 간단한 생성자다. 여기서 매개 변수 contextView가 실행될 때 현재 테마, 리소스 등을 구성하는데 사용된다.

  2. View(Context context, @Nullable AttributeSet attrs)
    XML에서 View를 전개(Inflate)할 때 호출되는 생성자로, XML 파일에서 지정된 속성을 제공하여 XML 파일에서 View를 구성 할 때 호출된다. 이 생성자는 기본 스타일을 사용하므로 Context의 테마 및 지정된 AttributeSet의 속성 값만 적용된다.

  3. View(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
    XML을 통해 전개(Inflate)를 하고 테마 속성에서 클래스별 기본 스타일을 적용한다. 이 생성자는 서브 클래스가 전개(Inflate)할 때 자체 기본 스타일을 사용할 수 있도록 한다.
    예를 들어, Button 클래스의 생성자는 수퍼 클래스 생성자를 호출하고 defStyleAttrR.attr.buttonStyle을 제공한다. 이를 통해 테마의 버튼 스타일은 모든 기본 View 속성 (특히 배경)과 Button 클래스의 속성을 수정할 수 있다.

  4. View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)
    XML을 전개하고 테마 속성 또는 Style 리소스에서 클래스 별 기본 스타일을 적용한다. 이 생성자는 서브 클래스가 전개(Inflate)할 때 자체 기본 스타일을 사용할 수 있도록한다. 위와 유사하다.
    매개변수 defStyleResViewdefStyleAttr가 0이거나 테마에서 찾을 수 없는 경우에만 기본값을 제공하는 Style 리소스 ID다. 기본값을 찾지 않으려면 0 으로 지정한다.

2. Attachment / Detachment

View가 Window에서 연결되거나 분리 될 때의 단계다. 이 단계에는 적절한 작업을 수행하기 위해 콜백을 받는 몇 가지 방법이 있다.

onAttachedToWindow()
View가 Window에 연결되면 호출된다. View가 활성화 될 수 있고, 드로잉 할 표면이 있음을 알고있는 단계다. 따라서 리소스 할당을 시작하거나 리스너를 설정할 수 있다.

onDetachedFromWindow()
View가 Window에서 분리 될 때 호출된다. 이 시점에서 더 이상 드로잉을 할 표면이 없다. 예약 된 자원을 정리하거나 정리하는 모든 종류의 작업을 중지해야하는 곳이다.
이 메소드는 ViewGroup에서 View 제거를 호출하거나 액티비티가 Destroyed될 때 호출된다.

onFinishInflate()
이 메소드는 View가 전개(Inflate)가 끝날 때 호출된다. 레이아웃의 경우 모든 Child View가 추가 된 후에 호출된다.

순회(Traversals)

View 계층 구조는 부모 노드(ViewGroup)에서 분기가 있는 리프 노드(Child Views)의 트리 구조와 같기 때문에 순회 단계라고 한다. 따라서 각 메소드는 부모에서 시작하여 마지막 노드까지 순회하여 제약 조건을 정의한다.

Measure 단계와 Layout 단계는 항상 위와 같이 순차적으로 진행된다.

3. onMeasure()

onMeasure() 메소드는 View의 크기를 확인하기 위해 호출된다. ViewGroup의 경우 계속해서 각 Child View에 대한 측정을 하고, 그에 대한 결과로 자신의 사이즈를 결정한다.

onMeasure(int widthMeasureSpec, int heightMeasureSpec)
// @param widthMeasureSpec 부모 View에 의해 적용된 수평 공간 요구사항
// @param heightMeasureSpec 부모 View에 의해 적용된 수직 공간 요구사항

onMeasure()는 값을 반환하지 않고, setMeasuredDimension()을 호출하여 너비와 높이를 명시적으로 설정한다.

MeasureSpec

MeasureSpec은 부모에서 자식으로 전달되는 레이아웃 요구 사항을 캡슐화한다. 각 MeasureSpec은 너비 또는 높이에 대한 요구 사항을 나타낸다. MeasureSpec은 크기와 모드로 구성되며, 세 가지 모드가 있다.

MeasureSpec.EXACTLY: 부모가 자식의 정확한 크기를 결정한다. 자식의 사이즈와 관계없이 주어진 경계 내에서 사이즈가 결정된다.

MeasureSpec.AT_MOST: 자식은 지정된 크기까지 원하는 만큼 커질 수 있다.

MeasureSpec.UNSPECIFIED: 부모가 자식에 제한을 두지 않기 때문에, 자식은 원하는 크기가 될 수 있다.

onLayout()

View 위치를 측정하여 화면에 배치 한 후에 호출된다.

onDraw()

크기와 위치는 이전 단계에서 계산되므로 View는 그것들을 기준으로 그려진다. onDraw(Canvas canvas) 메서드에서 생성된 캔버스 객체에는 GPU로 보낼 OpenGL-ES 명령목록(displayList)이 있다.
onDraw()여러번 호출되므로 이곳에서 객체를 만들면 안된다.

특정 View의 속성이 변경되었을 때 실행되는 두 가지 메서드가 있다.

invalidate()
invalidate()는 변경 사항을 보여주고자 하는 특정 View에 대해 강제로 다시 그리기를 요구하는 메소드이다. View 모양이 변경되면 invalidate()를 호출해야한다고 할 수 있다.

requestLayout()
어떤 시점에서 View의 경계가 변경되었다면, View를 다시 측정하기 위해 requestLayout()을 호출하여 Measure및 Layout 단계를 다시 거칠 수 있다.

View에서 메소드를 호출 할 때는 항상 UI 스레드내에서 수행해야한다. 다른 스레드에서 작업하고 있고, 해당 스레드에서 View의 상태를 업데이트 하려는 경우 핸들러를 사용해야한다.

profile
"비몽(Bemong)"이라는 앱을 개발 및 운영 중인 안드로이드 개발자입니다.

0개의 댓글