오늘은 Android Jetpack 중 View Binding을 사용하는 방법에 대해 알아보자.
View Binding의 필요성
기존에 Android에서 XMl에서 작성한 View의 요소를 불러오기 위해서는 findViewById
를 사용했다.
View의 요소가 많아지면 그만큼 findViewById
메서드를 호출해야 하므로 굉장히 번거로웠다.
이후에 Kotlin-android-extensions를 사용하면 findViewById
를 생략하고 간편하게 뷰에 접근할 수 있게 되었지만 문제는 서로 다른 레이아웃 XML 파일에서 id를 동일하게 사용할 수 있기 때문에 코드가 헷갈릴 가능성이 있었다.
이러한 문제들 때문에 Android에서는 Kotlin-android-extensions의 지원을 중단하고 View Binding을 사용하도록 안내하고 있다.
View Binding 이란?
View Binding 이란 findViewById
없이 레이아웃 XML의 각 View에 보다 쉽게 접근할 수 있는 기능이다.
View Binding을 활성화하면 각 XML 파일에 대해 ViewBinding 클래스를 상속받는 개별 ViewBinding 클래스가 자동으로 생성된다.onCreate()
메서드에서 ViewBidning 클래스의 인스턴스를 생성하며 인스턴스는 View의 Id를 프로퍼티로 제공한다.
View Binding은 Android Studio 3.6 Canary 11 이상에서 사용이 가능하다.
View Binding의 장점
- Null-safe: 뷰 바인딩은 서로 다른 레이아웃의 같은 Id를 가진 뷰를 구분할 수 있으며 그럴 수 없는 경우에는
@Nullable
로 만들어 사용할 수 없게한다. - Type-safe:
findViewById
를 사용할 경우 뷰에 잘못된 타입을 지정할 우려가 있지만 ViewBinding에서는 바인딩 클래스를 생성할 때 타입을 자동으로 지정하여 생성하므로 타입 문제가 발생하지 않는다.
위 두 장점 덕분에 레이아웃과 코드 사이의 비호환성이 발생하면 런타임이 아닌 컴파일 타임에 빌드를 실패하므로 미리 오류를 방지할 수 있다.
View Binding 세팅
View Binding을 사용하기 위해서는 앱 모듈 수준의 gradle 파일에 다음과 같이 코드를 추가한다.
android {
...
buildFeatures {
viewBinding true
}
}
바인딩 클래스 자동 생성을 원하지 않으면 tools:viewBindingIgnore="true"
코드를 최상단 레이아웃에 추가하면 된다.
View Binding 사용
모듈에 View Binding을 사용하도록 설정하면 해당 모듈에 포함된 각 XML 레이아웃 파일의 바인딩 클래스가 자동으로 생성된다.
바인딩 클래스의 이름은 XML 파일의 이름을 카멜 표기법으로 변환 후 끝에 Binding을 추가한다.
예를 들어 레이아웃 파일의 이름이 activity_main.xml
인 경우 ActivityMainBinding
으로 바인딩 클래스 이름이 정해진다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:src="@drawable/ic_launcher_foreground" />
<TextView
android:id="@+id/textview_main" />
<Button
android:id="@+id/button_main"
android:text="open fragment"/>
</androidx.constraintlayout.widget.ConstraintLayout>
위와 같이 activity_main.xml
파일이 작성된 경우 ActivityMainBinding
클래스 안에는 textviewMain
과 buttonMain
을 프로퍼티가 존재한다.
ImageView는 Id가 없으므로 프로퍼티가 따로 없다.
모든 바인딩 클래스에는 해당 레이아웃 파일의 루트 뷰에 대한 직접 참조를 제공하는 getRoot()
메서드가 포함되어있다.
Kotlin에서는 프로퍼티에 접근하듯 root를
사용하면 된다.
위 activity_main.xml
에서 getRoot()
메서드는 ConstraintLayout 뷰를 반환한다.
Activity에서 View Binding 사용
Activity에서 바인딩 클래스를 인스턴스화 하려면 onCreate
메서드에서 초기화를 진행한다.
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
- 생성된 바인딩 클래스에 포함된 정적 메서드
inflate()
를 호출한다.
-> 액티비티에서 사용할 바인딩 클래스 인스턴스가 생성된다. setContentView()
함수에 루트 뷰를 전달하여 화면상의 활성 뷰로 만든다.
-> 루트 뷰를 전달할 때binding.root
로 루트 뷰에 접근한다.
binding.textviewMain.text = "ViewBindingEx"
binding.buttonMain.setOnClickListener {
startActivity(Intent(this, SecondActivity::class.java))
}
binding을 통해 View의 Id로 접근하여 View를 참조할 수 있다.
바인딩 클래스의 프로퍼티의 이름은 id의 _를 기준으로 카멜 케이스로 작성된다.
Fragment에서 View Binding 사용
Fragment에서 바인딩 클래스를 인스턴스화 하려면 onCreateView()
메서드에서 초기화를 진행한다.
class SubFragment : Fragment() {
private var _binding: FragmentSubBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSubBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textviewSub.text = "ViewBidningEx"
binding.buttonSub.setOnClickListener {
requireActivity().onBackPressed()
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // 꼭 null 로 참조를 해제할 것
}
}
- 생성된 바인딩 클래스에 포함된 정적 메서드
inflate()
를 호출한다.inflate()
메서드를 사용할 때 LayoutInflater를 전달해야 한다.- 레이아웃이 이미 확장되어 있다면
bind()
를 호출하면 된다.
onCreateView
메서드에서 루트 뷰를 반환하여 화면상의 활성 뷰로 만든다.
루트 뷰를 반환할 때binding.root
로 루트 뷰에 접근한다.
주의해야 할 점은 Fragment는 뷰 보다 오래 지속되므로 Fragment의 onDestroyView()
메서드에서
_binding = null로 바인딩 클래스의 인스턴스 참조를 정리해야 한다.
이런 이유 때문에 var binding = null
, val binding
으로 선언하여 사용한다.
binding.textviewSub.text = "ViewBidningEx"
binding.buttonSub.setOnClickListener {
requireActivity().onBackPressed()
}
Activity에서 사용했던 것과 같이 binding을 통해 View의 Id로 접근하여 View를 참조할 수 있다.
Data Binding과 비교
View Binding과 Data Binding은 모두 뷰를 직접 참조하는 데 사용할 수 있는 바인딩 클래스를 생성하지만 차이점이 존재한다.
- 뷰 바인딩은 데이터 바인딩보다 어노테이션 프로세싱의 일부를 사용하기 때문에 더 빠르게 바인딩 클래스를 생성하므로 컴파일 시간이 더 짧다.
- 뷰 바인딩에서는 특별히 태그 된 XML 레이아웃 파일이 필요하지 않으므로 앱에서 더 신속하게 채택할 수 있다
- 앱 모듈 dependency에서 뷰 바인딩 사용 설정하면 모든 레이아웃의 뷰 바인딩이 작용으로 적용되기 때문이다.
- 뷰 바인딩은 레이아웃 변수 또는 레이아웃 표현식을 지원하지 않으므로 XML 레이아웃 파일에서 직접 동적 UI 콘텐츠를 선언하는 데 사용할 수 없다.
- 뷰 바인딩은 양방향 데이터 결합을 지원하지 않는다.
전체 코드는 여기서 참고할 수 있습니다.
https://github.com/SangWoo-Han97/AndroidPractice/tree/main/ViewBindingEx
GitHub - SangWoo-Han97/AndroidPractice: 안드로이드 연습입니다.
안드로이드 연습입니다. Contribute to SangWoo-Han97/AndroidPractice development by creating an account on GitHub.
github.com
참고
https://developer.android.com/topic/libraries/view-binding
https://www.inflearn.com/course/%EC%95%8C%EA%B8%B0%EC%89%AC%EC%9A%B4-modern-android