[젯팩 컴포즈로 개발하는 안드로이드 UI] 2장 선언적 패러다임 이해
2장 : 선언적 패러다임 이해
뷰 기반과 컴포저블 함수와 유사점과 차이점
컴포저블 함수가 컴포넌트 중심 프레임워크의 한계를 어떻게 극복하는지
1. 안드로이드 뷰 시스템 살펴보기
기존 접근 방식
컴포넌트 트리를 정의하고 런타임에서 변경하는 것.
레이아웃 xml 파일에서 어떤 UI를 화면에 나타낼 지 정의함.
ConstraintLayout 내부에 TextView가 있는 경우 (레이아웃 파일은 계층구조(트리)를 정의한다)
→ 루트 노드 : ConstraintLayout, 자식 노드 : TextView
실제 앱은 여러 자식노드를 갖는 훨씬 더 중첩적인 구조가 된다.
레이아웃 + 비레이아웃 요소 ⇒ 컴포넌트
1.1. 레이아웃 파일 인플레이팅
뷰 기반 방식
Activity 내에서 onCreate() 매서드는 앱을 준비하고 setContentView()를 호출해 UI를 나타냄.
setContentView() 는 레이아웃 파일의 ID를 전달 받음. setContentView(R.layout.main)
접근하려는 UI요소를 가리키는 변수 정의
private lateinit var doneButton: Button
...
val doneButton = findViewById(R.id.done)
문제점
1️⃣ 변수를 초기화 하기 전에 접근하면 런타임에서 크래시가 발생할 여지가 있음
2️⃣ 컴포넌트 개수가 많아지면 코드는 급격히 비대해진다.
해결법 : 로컬 변수를 사용하여 UI 요소를 정의한 후 바로 접근하기.
val doneButton = findViewById<Button>(R.id.done)
문제점
블록문이나 함수 내부와 같이 변수를 정의한 범위 안에서만 존재하게 된다.
onCreate() 매서드 바깥에서 컴포넌트를 변경해야 하는 경우가 빈번하게 발생
해결법 : 뷰 바인딩 기능을 사용해서 컴포넌트의 참조를 계속 유지하기
class MainActivity: AppCompatActivity() {
**private lateinit var binding: MainBinding**
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
**binding = MainBinding.inflate(layoutInflater)
setContentView(binding.root)**
...
enableOrDisableButton()
}
}
UI가 아무리 복잡하더라도 한 개의 참조만 유지하면 된다.
1.2. UI 수정
enableOrDisableButton()
: 버튼을 활성화하거나 비활성화 하는 함수
private fun enableOrDisableButton() {
binding.done.isEnable = binding.name.text.isNotBlank()
}
사용자가 무언가를 입력할 때마다 이 함수를 다시 호출해야 함.
- 텍스트 필드 name
binding.name.run {
**setOnEditorActionListener** { _, _, _ ->
binding.done.performClick() // 완료 버튼을 클릭한 것처럼 처리
true
}
doAfterTextChanged { // 사용자가 텍스트 입력 필트에 무언가를 입력하거나 삭제할 때마다 호출됨.
enableOrDisableButton()
}
visibility = VISIBLE
}
엔터키를 클릭했을 때 반응하는려면 setOnEditorActionListener()를 호출해 코드를 등록해야 함.
setOnEditorActionListener의 람다 함수 내부에서 performClick()을 호출하는 이유 : 코드를 리팩토링해 함수로 분리한 후 해당 함수를 호출하지 않고도 버튼 리스너 내부에 있는 코드를 재사용할 수 있다.
binding.done.performClick() : 완료 버튼을 클릭한 것처럼 처리
doAfterTextChanged() : 사용자가 텍스트 입력 필트에 무언가를 입력하거나 삭제할 때마다 호출됨.
visibility = VISIBLE : 텍스트 입력 필드를 화면에 보이게 함
- 완료 버튼 done
binding.done.run {
setOnClickListener {
val name = binding.name.text
if (name.isNotBlank()) { // 공백 이외에 한 개 이상 문자를 포함하는지 여부 검사
binding.message.text = getString(R.string.hello, name)
binding.name.visibility = GONE
it.visibility = GONE
}
}
visibility = VISIBLE
}
- UI는 XML 파일로 정의한다
- UI는 런타임 단계에서 컴포넌트 트리로 인플레이트한다.
- UI를 변경하려면 연관된 모든 컴포넌트의 속성을 수정해야 한다.
- UI 요소가 화면에서 보이지 않더라도 컴포넌트 트리의 요소로는 남아있다.
→ 일반적인 UI 프레임워크를 명력적이라고 하는 이유.