
참고자료(https://developer.android.com/develop/ui/views/layout/custom-views/custom-drawing)
- 커스텀 뷰의 핵심: 도화지를 선택(onMeasure), 어느 위치에(onLayout), 어떤 그림을 그릴지(onDraw)를 설정해준다
Construnctor
- 최대 4개의 생성자를 가질 수 있음
- View(context: Context): 동적으로 뷰를 생성할 때 사용할 수 있는 간단한 생성자로 파라미터 context를 통해 현재 실행 중인 뷰의 리소스 등에 액세스 할 수 있음
- View(context: Context, attrs: AttributeSet): xml에서 생성할 떄
- View(context: Context, attrs: AtrributeSet, defStyleAttr: Int): ThemeStyle과 함께 뷰를 생성할 때 → 이 생성자는 API 21에 나옴
- View(context: Context, attrs: AtrributeSet, defStyleAttr: Int, defStyleRes: int): ThemeStyle 또는 Style로 xml에서 뷰를 생성할 때
onMeasure
-
해당 커스텀 뷰의 사이즈를 지정해줘야 함
-
xml에서 유저가 설정한 width, height의 정보가 파라미터로 넘어옴
-
MeasureSpec.getMode(~)를 통해 MATCH_PARENT, WRAP_CONTENT 또는 100dp와 같은 지정된 값인지 알 수 있음
-
여러 번 호출 가능함
- 부모가 자식들의 각 크기를 측정한 후 자식들의 크기 합이 크거나 작다면 다시 measure메소드가 호출하여 구체적인 값을 구함
- child view를 가지는 커스텀 뷰면 child의 사이즈를 측정해서 자신의 사이즈를 재야할 수 있음 이때 onMeasure 메소드에서 설정해주면 됨
- 파라미터로 넘어오는 widthMeasureSpec과 hegihtMeasureSpec은 뷰의 모드와 사이즈를 조합한 값
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
- 이 코드에서 모드와 사이즈를 알 수 있음
- 모드
- MeasureSpec.AT_MOST: wrap_content. 해당 값보다 더 클 수 없음, 측정 과정이 다시 발생할 가능성이 있음
- MeasureSpec.EXACTLY: match_parent, 500dp와 같이 정해져 있는 값, 측정 과정이 발생하지 않음
- MeasureSpec.UNSPECIFIED: 정해 있지 않은 값, 원하는 값을 설정할 수 있음, 측정 과정이 다시 발생할 가능성이 있음
-
onMeasure() 끝나면 setMeasureDimension()을 통해 값을 설정해줌
-
뷰는 전위순회 방식으로 그려진다. 그런데 부모 뷰가 자식 뷰의 크기에 따라 자신의 크기를 조정한다면 아직 자식 뷰를 측정하지 않았는데 어떻게 자신의 사이즈를 측정할 수 있을까?
⇒ measureChildren(widthMeasureSpec: Int, heightMeasureSpec: Int) 메소드를 사용하면 됨
해당 뷰의 모든 자식 뷰들에게 지정된 MeasureSpec 조건과 패딩을 고려하여 각각 자체적으로 크기를 측정하도록 요청함
onLayout
- 뷰의 위치를 설정해주는 함수
- 뷰의 child들의 크기와 위치를 할당해야할 떄 호출됨
- 즉 child를 가지는 뷰라면 해당 메소드를 오버라이드를 해주어야 함
- 이때 넘어오는 값들은 앱 전체를 기준으로 넘어오는 위치 값
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
children?.forEachIndexed { index, view ->
view.layout(x, y x + view.measuredWidth, y + view.measuredHeight)
}
}