Activity가 라이프사이클을 가지고 있는 것 처럼 뷰도 라이프사이클을 가지고 있다.
여기서 뷰란? 우리가 앱을 실행하면 보이는 것 전부가 View라고 할 수 있다. View는 Button, TextView, ImageView등의 위젯을 작성하는데 사용되는 기본 클래스이다. View의 또 다른 서브클래스인 ViewGroup은 보이지 않는 컨테이너로써 다른 View들을 다른 View(또 다른 ViewGroup)을 포함할 수 있다. (View와 ViewGroup이 궁금하다면 클릭)
화면에 렌더링 된 View는 아래 그림과 같은 Lifecycle를 거쳐 화면에 그려진다.
Counstructors
우리는 Custom View를 만들 때 생성자를 사용하는데 어떤 생성자를 구현 해야하는지 혼란스러울 때가 있다. 자세히 알아보자.
View(Context context)
View(Context context, @Nullable AttributeSet attrs)
View(Context context, @Nuallable AttributeSet attrs, int defStyleAttr)
View(Context context, @Nuallable AttributeSet attrs, int defStyleAttr, int defStyleRes)
View(Context context)
-코드에서 View를 동적으로 만들 때 사용하는 간단한 Counstructors이다. 여기서 매개변수 context는 View가 실행될 때 현재 테마, 리소스 등을 구성하는데 사용된다.
View(Context context, @Nullable AttributeSet attrs)
-XML에서 View를 Inflation 할 때 호출되는 생성자로, XML 파일에서 지정된 속성을 제공하여 XML 파일에서 View를 구성할때 호출된다. 이 생성자는 기본 스타일인 0을 사용하므로 context의 테마 및 지정된 AttributeSet의 속성 값만 적용된다.
View(Context context, @Nuallable AttributeSet attrs, int defStyleAttr)
-XML을 통해 전개를 하고 테마 속성에서 클래스별 기본 스타일을 적용한다. 이 View 생성자는 서브 클래스가 전개할 때 자체 기본 스타일을 사용할 수 있도록 한다. 예를들어, Button 클래스의 생성자는 수퍼 클래스 생성자를 호출하고 defStyleAttr에 R.attr.buttonStyle을 제공한다. 이를 통해 테마의 버튼 스타일은 모든 기본 View 속성(특히 배경)과 Button 클래스의 속성을 수정할 수 있다. defStyleAttr 매개변수는 View의 기본 값을 제공하는 Style 리소스 대한 참조를 포함하는 현재 테마의 속성이다. 기본값을 찾지 않으려면 0으로 지정할 수 있다.
View(Context context, @Nuallable AttributeSet attrs, int defStyleAttr, int defStyleRes)
-XML을 전개하고 테마 속성 또는 Style 리소스에서 클래스 별 기본 스타일을 적용한다. 이 생성자는 서브 클래스가 전개할 떄 자체 기본 스타일을 사용할 수 있도록 한다. 3번과 유사하다.
매개변수 defStyleRes는 View의 defStyleAttr가 0이거나 테마에서 찾을 수 없는 경우에만 기본값을 제공하는 Style 리소스 ID다. 기본값을 찾지 않으려면 0으로 지정한다.
Attachment / Detachment
-View가 Window에서 연결(Attachment)되거나 분리(Detachment)될 떄의 단계
-이 단계에서는 적절한 작업을 수행하기 위해 콜백을 받는 몇가지 방법이 있다.
onAttachedToWindow()
-View가 Window에 연결되면 호출된다. View가 활성화 될 수 있고, 드로잉 할 표면이 있음을 알고있는 단계다. 따라서 리소스 할당을 시작하거나 리스너를 설정할 수 있다.
onDetachedFromWindow()
-View가 Window에서 분리 될 때 호출된다. 이 시점에서 더 이상 드로잉을 할 표면이 없다. 예약된 자원을 정리하거나 정리하는 모든 종류의 작업을 중지 해야하는 곳이다. 이 메소드는 ViewGroup에서 View제거를 호출하거나 액티비티가 Destroyed될 때 호출힌다.
onFinishInflate()
-이 메소드는 View가 전개가 끝날 때 호출된다. 레이아웃의 경우 모든 Child View가 추가 된 후에 호출된다.
Traversals(순회)
-View 계층 구조는 부모노드(ViewGroup)에서 분기가 있는 리프노드(Child Views)의 트리 구조와 같이 때문에 순회 단계라고 한다.
Animate → Measure → Layout → Draw
onMeasure()
-View의 크기를 확인하기 위해 호출된다. ViewGroup의 경우 계속해서 각 Child View에 대한 측정을 하고, 그에 대한 결과로 자신의 사이즈를 결정한다.
onMeasure(int widthMeasureSpec, int heightMeasureSpec)
@param widthMeasureSpec 부모뷰에 의해 적용된 수평 공간 요구사항
@param heightMeasureSpec 부모뷰에 의해 적용된 수직 공간 요구사항
onMeasure()는 값을 반환하지 않고, setMeasuredDimension()을 호출하여 너비와 높이를 명시적으로 설정한다.
MeasureSpec
-MeasureSpec은 부모에서 자식으로 전달되는 레이아웃 요구사항을 캡슐화한다. 각 MeasureSpec은 너비 또는 높이에 대한 요구사항을 나타낸다. MeasureSpec은 크기와 모드로 구성되며, 세 가지 모드가 있다.
MeasureSpec.EXACTLY
⇒ 부모뷰가 자식뷰의 정확한 크기를 결정한다. 자식뷰의 사이즈와 관계없이 주어진 경계내에서 사이즈가 결정된다.
MeasureSpec.AT_MOST
⇒ 자식뷰는 지정된 크기까지 원하는 만큼 커질 수 있다.
MeasureSpec.UNSPECIFIED
⇒ 부모뷰가 자식뷰에 제한을 두지 않기 때문에, 자식뷰는 원하는 크기가 될 수 있다.
onLayout()
-뷰를 측정하여 화면에 배치한 후에 호출된다.
onDraw()
-크기와 위치는 이전 단계에서 계산되므로 View는 그것들을 기준으로 그릴 수 있다. onDraw(Canvas)메서드에서 생성된 캔버스 객체에는 GPU로 보낼 OpenGL-ES 명령목록(displayList)이 있다. onDraw()는 여러번 호출되므로 이곳에서 객체를 만들면 안된다.
특정 뷰의 속성이 변경되었을 때 실행되는 두 가지 메서드가 있다(invalidate(), requestLayout())