개발일지

Android in A..Z - View (Draw Step) 본문

Android (안드로이드)/View

Android in A..Z - View (Draw Step)

강태종 2021. 10. 4. 16:04

View가 그려지는 순서는 measure -> layout -> draw 과정을 거친다. 또한 전위순회 방식(부모를 그리고 자식을 형제순으로) 으로 그린다.


Measure

measure(widthMeasureSpec: Int, heightMeasureSpec: Int)

View의 크기를 측정하는 과정이다. final 함수이지만 내부적으로 onMeasure를 호출하고 onMeasure를 재정의하여 사용할 수 있다. 아직 ChildView는 measure된 상태가 아니기 때문에 width, measuredWidth 는 0이 반환될 수 있다.

 

setMeasureDimension(measuredWidth: Int, measuredHeight: Int)

View의 크기를 측정한다. onMeasure에서 필수적으로 호출해야 한다. (super.onMeasure에서도 setMeasureDimension을 호출한다.)

MeasureSpectMode가 EXACTLY인 경우 MeasureSpect에서 전달 받은 Size를 사용하고, AT_MOST인 경우 전달 받은 Size가 최대 크기를 의미한다. 이를 계산하여 setMeasureDimension을 호출하면 된다.

 

MeasureSpec

MeasureSpec은 Mode와 Size에 대한 정보를 Bit로 가지고 있다.

val widthMode: Int = MeasureSpec.getMode(widthMeasureSpec)
val widthSize: Int = MeasureSpec.getSize(widthMeasureSpec)
val heightMode: Int = MeasureSpec.getMode(heightMeasureSpec)
val heightSize: Int = MeasureSpec.getSize(heightMeasureSpec)

 

MeasureSpecMode

View의 크기가 유동적인 경우 크기를 측정할 수 있게 도와준다. (대부분의 View는 크기가 고정되지 않고 ParentView와 LayoutParams를 통해 결정되기 때문에 유동적인 측정이 필요하다.)

  • EXACTLY : 고정값이다. match_parent 또는 100dp 같은 경우 (match_parent가 EXACTLY인 이유는 전위순회 방식으로 View가 그려지기 때문에 ParentView가 크기가 먼저 정해지면서 특정한 값을 같기 때문.)
  • AT_MOST : 가변값이다. wrap_content인 경우
  • UNSPECIFIED : 정해지지 않음. layout 과정에서 재발생

Layout

layout(left, top, right, bottom)

부모와 자식뷰의 크기와 위치를 할당한다. 이 시점에서 width와 height의 크기가 정해진다. 또한 measuredWidth와 measuredHeigt을 measure함수를 호출하여 다시 측정하여 바꿀 수 있다. (UNSPECIFIED인 경우나 View의 크기가 너무 작거나 큰 경우 다시 측정할 수 있음)


Draw

onDraw(canvas: Canvas)

Canvas와 Paint로 그래픽을 그린다. 자주 호출되기 때문에 onDraw 함수 내에 인스턴스를 생성하는 것은 메모리 누수를 초래할 수 있다.

* onDraw는 Animation처리나, View를 업데이트할 때마다 호출된다. 함수 내에서 인스턴스를 생성하는 경우, 인스턴스가 메모리에 올라가고 GC가 회수하는 과정에서 GC가 처리해야할 인스턴스가 많아지면서 메모리 누수를 발생시킨다.

invalidate() vs requestLayout()

invalidate는 Draw 작업만 하지만 requestLayout은 Measure, Layout, Draw 작업을 한다. View를 업데이트할 때 상황에 맞게 활용하여 리소스를 아낄 수 있다.


 

Comments