findViewByiD<View>(id: Int)
함수는 root 뷰에서부터 순차적으로 탐색해가면서 id 에 해당하는 뷰를 찾는다. 그 때문에 뷰가 많아지면 앱의 성능이 저하될 수 밖에 없다. 또한 type safe 하지 않아서 런타임 에러를 유발할 수 있다.
그에 비해 뷰 바인딩은 초기에 바인딩 객체
를 하나 만들고 이 객체를 이용해서 뷰에 접근하므로 성능상 이점이 있고, 컴파일 타임에 null pointer, class cast 예외를 잡아낼 수 있다.
이러한 바인딩 객체는 각 레이아웃 파일마다 하나씩 생성된다. 레이아웃에 포함된 뷰 들은 바인딩 객체의 프로퍼티로 자리잡고 있다.
만약 특정 레이아웃에 대해 뷰 바인딩을 사용하지 않을 것이라면 루트 뷰에
tools:viewBindingIgnore="true"
를 명시하면 된다.
액티비티 컴포넌트는 onCreate() 함수에서 뷰에 접근할 수 있다. 그리고 destroy 될때까지 이러한 뷰에 접근할 수 있다. 그렇기 때문에 onCreate() 부터 onDestroy() 까지, 그러니까 액티비티의 모든 라이프싸이클에서 뷰에 접근할 수 있다.
하지만 프레그먼트는 그렇지 않다. 프레그먼트는 onCreateView() 에서부터 onDestroyView() 사이의 라이프 사이클에서 뷰에 접근할 수 있다.
하지만 onCreateView 는 프레그먼트의 첫번째 함수가 아니다. onCreate 가 프레그먼트이 첫번째 함수이다. 그렇기 때문에 프레그먼트는 onCreate() 시점에서 뷰와 상호작용을 할 수 없다.
onDestroyView 도 비슷하다. 그 다음에 Destroy() 함수가 있고, 이 함수는 뷰에 접근할 수 있다.
class HomeFragment: Fragment() {
private var _binding : FragmentHomeBinding? = null
// non-null access 를 보장, 이렇게 하면 lateinit var 랑 비슷해짐
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
// 더이상 뷰에 접근할 수 없으니 null 로 하여 이후에 나올 수 있는 실수는 컴파일 에러에서 잡히도록 함
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}