ViewBinding을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있습니다.
각 xml파일에 대해 viewBinding클래스를 상속 받는 개별 뷰 바인딩 클래스가 자동으로 생성됩니다.
기존 방식은 뷰의 요소를 불러오기 위해 findViewById를 사용해야합니다.
kotlin-extensions를 사용하면 생략하고 간편하게 쓸 수 있지만 서로 다른 xml에서 id를 동일하게 사용하면 헷갈릴 수 있습니다.
그래서 구글에서는 kotlin-extensions 지원을 중단하고 뷰 바인딩을 사용하도록 권장하고 있습니다.
- Null-Safety
뷰 결합은 뷰의 직접 참조를 생성하므로 유효하지 않은 뷰 ID로 인해 null 포인터 예외가 발생할 위험이 없습니다. 또한 레이아웃의 일부 구성에만 뷰가 있는 경우 결합 클래스에서 참조를 포함하는 필드가 @Nullable로 표시됩니다.
- Type-Safety
각 바인딩 클래스에 있는 필드의 유형이 XML 파일에서 참조하는 뷰와 일치합니다. 즉, 클래스 변환 예외가 발생할 위험이 없습니다.
android {
...
viewBinding{
enabled = true
}
}
app수준의 build.gradle파일에 viewBinding을 사용하겠다고 선언해준다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_move"
android:text="SecondActivity 이동"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
xml파일에서는 기존에 작성하던 방식 그대로 사용한다.
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tvMain.text = "메인 액티비티"
binding.btnMove.setOnClickListener {
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
}
}
xml파일을 만들면 자동으로 ActivitymainBinding이라는 클래스가 생성되고 이 클래스 타입의 binding변수를 만들어준다
binding 변수에 inflate를 한 후 SetContentView에 binding.root를 넣어준다
이후에는 binding변수를 통해 xml에서 정의한 id값으로 뷰를 참조할 수 있다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">
<TextView
android:id="@+id/tv_first_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="first_fragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_move_secondFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SecondFragment로 이동"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_first_fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
xml 사용은 기존과 동일하다
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnMoveSecondFragment.setOnClickListener {
findNavController().navigate(R.id.action_firstFragment_to_secondFragment)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
fragment도 activity와 동일하게 inflate를 시켜주는 방식이지만 다른 점이 있다.
onDestroyView에서 _binding을 null 처리해주는 것인데 이 이유는 프래그먼트는 뷰보다 오래 지속되므로 onDestroyView에서 결합 클래스 인스턴스 참조를 정리해야 합니다.