View 그려지는 과정
- UI 를 그리는 기본 구성요소
- CustomView 를 만들기 위함
안드로이드는 유저 인터페이스인 UI 를 통해서
사용자와 앱이 인터랙션을 하게 된다.
따라서 UI 를 그리는 View 와
View 가 그려지는 과정이 중요하다.
0) 전위순회 방식
전위순회 방식을 쓰기 때문에, 부모 부터 자식 뷰 순서로 그려지게 됨
현재 Depth 는 2
Depth 가 깊어질 수록
확인해야하는 내용이 타고 타고 타고 들어가면서 많아진다.
이러한 이유로, View 를 Flat 한 구조로 가져가라고 한다.
그래야 렌더링 속도가 빨라지기 때문
1) measure
- 뷰의 크기를 계산
- 모든 뷰는 각각 자신의 width, height 를 계산
- measure 과정에서, 부모 - 자식 뷰간의 크기 정보 전달을 위해 2가지 클래스 사용
- ViewGroup.LayoutParams
- DP, PX.. : 자식뷰가 원하는 사이즈
- MATCH_PARENT : 부모 뷰 사이즈와 똑같이 자식뷰 사이즈 지정
- WRAP_CONTENT : 부모 뷰 안에서, content 를 표현할 수 있는 fit 한 사이즈 지정
- 자식 뷰가 부모 뷰에게 자신이 어떻게 측정되고 위치를 정할지 요청 할 때 사용, (how big)
- ViewGroup.MeasureSpecs
- UNSPECIFIED : 부모 뷰는 자식 뷰가 원하는 사이즈로 결정
- EXACTLY : 부모 뷰가 자식 뷰의 사이즈를 정확히 지정할 때
- AT_MOST : 부모 뷰가 자식 뷰의 최대 사이즈를 지정할 때
- 부모 뷰가 자식 뷰에게 요구사항을 전달할 때 사용
2) layout
- 뷰의 크기와 위치를 할당
- 부모기준의 상대적 위치 (left, top, right, bottom) 을 계산
3) draw
- 뷰를 그리는 단계
- Canvas : 뷰의 모양을 그리는 객체
- Paint : 뷰의 색상을 칠하는 객체
- measure, layout 에서 측정한 크기와, 계산한 위치에 뷰를 그림
- 이 콜백은 언제든 다시 호출 될 수 있음
- scroll 이나 swipe 를 하게 되면 뷰는 onDraw 다시 호출
- 객체 할당과 같이 리소스가 많이 소모되는 로직은 추가하지 말 것
- ViewUpdate : 런타임에 뷰를 다시 그리게 하는 함수
- invalidate : view 에 변화가 생겨서 다시 그려야 할 때
- color 변화 등
- requestLayout : view 를 처음부터 그려야 할 때
- 크기가 변화해서 measure 부터 다시 해야할 때
- invalidate : view 에 변화가 생겨서 다시 그려야 할 때
실습
1) CustomLinearLayout.kt
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.util.Log
import android.widget.LinearLayout
class CustomLinearLayout @JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
LinearLayout(context, attrs, defStyleAttr) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
Log.i("그리기", "CustomLinearLayout - onMeasure")
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
Log.i("그리기", "CustomLinearLayout - onLayout")
super.onLayout(changed, l, t, r, b)
}
override fun onDraw(canvas: Canvas?) {
Log.i("그리기", "CustomLinearLayout - onDraw")
super.onDraw(canvas)
}
}
2) CustomTextView.kt
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.util.Log
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.widget.AppCompatTextView
class CustomTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
AppCompatTextView(context, attrs, defStyleAttr) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
Log.i("그리기", "CustomTextView - onMeasure")
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
Log.i("그리기", "CustomTextView - onLayout")
super.onLayout(changed, l, t, r, b)
}
override fun onDraw(canvas: Canvas?) {
Log.i("그리기", "CustomTextView - onDraw")
super.onDraw(canvas)
}
override fun requestLayout() {
Log.i("그리기", "CustomTextView - requestLayout")
super.requestLayout()
}
override fun invalidate() {
Log.i("그리기", "CustomTextView - invalidate")
super.invalidate()
}
}
3) MainActivity.kt
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<TextView>(R.id.textView).setOnClickListener { (it as TextView).setTextColor((resources.getColor(R.color.purple_200, null))) }
findViewById<TextView>(R.id.textView).setOnLongClickListener {
(it as TextView).text = "test"
true
}
}
}
4-1) 실행 결과
부모뷰인 LinearLayout 에서 Measure 이 호출된 다음,
TextView 에서 호출됨
onLayout 위치 정보, 크기를 지정하는 부분까지 호출된 다음
마지막으로 onDraw 가 호출된다.
onDraw 단계에서 화면에 그림을 그린다.
LinearLayout 에서 onDraw 가 호출되지 않는 이유는
LinearLayout 은 그림을 그리는 것이 아니라
그림을 그릴 “틀” 역할을 하기 때문이다.
4-2) 색이 변경될 때 ( invalidate )
클릭했을 때, text 색이 변경되는 기능
invalidate
view 에 변화가 생겨서 다시 그려야 할 때
color 변화 등
4-3) 뷰의 크기와 위치가 변경될 때
롱클릭 했을 시, hello world 글씨를 test 로 바꾸는 기능
뷰의 크기와 위치가 바뀐다.
텍스트뷰의 내용이 변경되고
안에 들어가있는 width 나 height 관련 정보가 모두 wrapcontent 이기 때문이다.
requestLayout
view 를 처음부터 그려야 할 때
크기가 변화해서 measure 부터 다시 해야할 때
invalidate
view 에 변화가 생겨서 다시 그려야 할 때
color 변화 등
measure
뷰의 크기를 계산
모든 뷰는 각각 자신의 width, height 를 계산
layout
뷰의 크기와 위치를 할당
부모기준의 상대적 위치 (left, top, right, bottom) 을 계산
'안드로이드 앱(Kotlin|Java) > [2025~] 안드로이드 앱' 카테고리의 다른 글
Part0_Ch04_03 ktlint 적용해보기 (0) | 2025.02.03 |
---|---|
Part0_Ch04_02 Android Studio 살펴보기 (0) | 2025.02.03 |
Part0_Ch03_04 Activity 생명주기 (2) (0) | 2025.02.03 |
Part0_Ch03_03 Activity 생명주기 (1) (0) | 2025.02.03 |
Part0_Ch03_02 Android 기본 요소 (0) | 2025.02.03 |